ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/ns_dev/Python/NinoCode/Active_prgs/Redgrave/EndoIGServerAnalyzer.py
Revision: 793
Committed: Fri Sep 8 15:05:08 2023 UTC (2 years, 6 months ago) by nino.borges
Content type: text/x-python
File size: 22918 byte(s)
Log Message:
Added support for other Domains SQL Servers Matrix.

File Contents

# User Rev Content
1 nino.borges 771 """
2    
3     EndoIGServerAnalyzer
4    
5     Created by:
6     Emanuel Borges
7     07.28.2022
8    
9     This program will take two CSV files and do a row to row compare. For this to work both CSVs need to have the same colums.
10     Set colOmitList to FALSE if you dont want it to parse at all and remove any columns. for that to work though you have to normalize the date
11     and number fields.
12    
13     NOTE: the application List file HAS pipes in it, so carefully select which columns you use.
14    
15     """
16    
17     import os
18    
19    
20     if __name__ == '__main__':
21    
22 nino.borges 793 outputFileName = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\20221129-WindowsServer8_Matrix_Report.csv"
23 nino.borges 771
24     ## This should all be csvs with pre-selected fields that are verified didnt already contain pipes. Also a headder row.
25     #win2kServersFile = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\_fromEndo\_csv\Win8Servers_filtered.csv"
26     win2kServersFile = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\_fromEndo\_csv\Win8Servers_NOFilter.csv"
27    
28     appListServersFile = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\_fromEndo\_csv\AppInventory_extraFields.csv"
29     sqlServersListFile = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\_fromEndo\_csv\Endo SQL Servers List__As of July 2022.csv"
30     prevSqlServersListFile = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\_fromEndo\_csv\SQL Server Report 07212021.csv"
31     aspectDatabasesToServersFile = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\_fromEndo\_csv\All_endo_SQL_Server_databases.csv"
32 nino.borges 783 serviceNowToServersFile = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\_fromEndo\_csv\ServiceNow-PreParsed.txt"
33 nino.borges 786 legacySnowToServersFile = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\_fromEndo\_csv\LegacySNOW-PreParsed.txt"
34 nino.borges 789 win2kChngReqToServersFile = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\_fromEndo\_csv\SN-FromWinFile-PreParsed.txt"
35 nino.borges 791 crawledSqlServersListFile = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\_fromEndo\_csv\SqlServerDatabaseDetails-08-17-2022-21h44m56s-DatabaseSummaryONY.csv"
36 nino.borges 793 otherDomainsSqlServersListFile = r"C:\Users\eborges\Documents\Cases\Endo\20220715 - IG Projects\4 - Develop Identification Process to Inventory NCDS stored in Endo IT\_fromEndo\_csv\MSAPT-OtherDomains(databaseSummaryOnly).csv"
37 nino.borges 771
38     win2kServersFileMatrix = {}
39     appListServersFileMatrix = {}
40     sqlServersListFileMatrix = {}
41     prevSqlServersListFileMatrix = {}
42     aspectDatabasesToServersMatrix = {}
43 nino.borges 783 serviceNowToServersMatrix = {}
44 nino.borges 786 legacySnowToServersMatrix = {}
45 nino.borges 789 win2kChngReqToServersMatrix = {}
46 nino.borges 791 crawledSqlServersMatrix = {}
47 nino.borges 793 otherDomainsSqlServersMatrix = {}
48 nino.borges 771
49     ## Start with ingesting the win2kservers file but be sure to save the headders on this one.
50     contents = open(win2kServersFile).readlines()
51     win2kServersFileHeadders = contents[0]
52     win2kServersFileHeadders = win2kServersFileHeadders.replace("\n","")
53     contents = contents[1:]
54     for line in contents:
55     line = line.replace("\n","")
56     parsedLine = line.split("|")
57     if parsedLine[0].upper() in list(win2kServersFileMatrix.keys()):
58     win2kServersFileMatrix[parsedLine[0].upper()].append(line)
59     else:
60     win2kServersFileMatrix[parsedLine[0].upper()] = [line,]
61     print("There are %s servers to start with."%len(list(win2kServersFileMatrix.keys())))
62    
63    
64     ## Next ingest the SQL servers list
65     contents = open(sqlServersListFile).readlines()
66     contents = contents[1:]
67     for line in contents:
68     line = line.replace("\n","")
69     parsedLine = line.split("|")
70     if parsedLine[0].upper() in list(sqlServersListFileMatrix.keys()):
71     sqlServersListFileMatrix[parsedLine[0].upper()].append(line)
72     else:
73     sqlServersListFileMatrix[parsedLine[0].upper()] = [line,]
74     print("There are %s servers on the SQL Servers file."%len(list(sqlServersListFileMatrix.keys())))
75    
76    
77     ## Next ingest the older SQL servers list
78     contents = open(prevSqlServersListFile).readlines()
79     contents = contents[1:]
80     for line in contents:
81     line = line.replace("\n","")
82     parsedLine = line.split("|")
83     if parsedLine[0].upper() in list(prevSqlServersListFileMatrix.keys()):
84     prevSqlServersListFileMatrix[parsedLine[0].upper()].append(line)
85     else:
86     prevSqlServersListFileMatrix[parsedLine[0].upper()] = [line,]
87     print("There are %s servers on the older SQL servers file."%len(list(prevSqlServersListFileMatrix.keys())))
88    
89    
90     ## Next ingest the databasename to server list from aspect
91     contents = open(aspectDatabasesToServersFile).readlines()
92     contents = contents[1:]
93     for line in contents:
94     line = line.replace("\n","")
95     parsedLine = line.split("|")
96 nino.borges 791 sName = parsedLine[0]
97     sName = sName.split(".")[0]
98     if sName.upper() in list(aspectDatabasesToServersMatrix.keys()):
99     aspectDatabasesToServersMatrix[sName.upper()].append(line)
100 nino.borges 771 else:
101 nino.borges 791 aspectDatabasesToServersMatrix[sName.upper()] = [line,]
102    
103     ## if parsedLine[0].upper() in list(aspectDatabasesToServersMatrix.keys()):
104     ## aspectDatabasesToServersMatrix[parsedLine[0].upper()].append(line)
105     ## else:
106     ## aspectDatabasesToServersMatrix[parsedLine[0].upper()] = [line,]
107 nino.borges 771 print("There are %s servers on the aspect databases to servers file."%len(list(aspectDatabasesToServersMatrix.keys())))
108    
109     ## Next is the app inventory which must first be parsed.
110     contents = open(appListServersFile).readlines()
111     contents = contents[1:]
112     for line in contents:
113     line = line.replace("\n","")
114     parsedLine = line.split("|")
115     parsedServerList = parsedLine[4].replace("#","")
116     parsedServerList = parsedServerList.split(";")
117     for s in parsedServerList:
118     if s.isnumeric():
119     pass
120     else:
121 nino.borges 793 if "." in s:
122     s = s.split(".")[0]
123 nino.borges 771 if s.upper() in list(appListServersFileMatrix.keys()):
124     appListServersFileMatrix[s.upper()].append(line)
125     else:
126     appListServersFileMatrix[s.upper()] = [line,]
127     print("There are %s servers on the application list."%len(list(appListServersFileMatrix.keys())))
128    
129 nino.borges 783 ## Next ingest the ServiceNow Export to server list. check that there is a chg number at all.
130     contents = open(serviceNowToServersFile).readlines()
131     contents = contents[1:]
132     for line in contents:
133     line = line.replace("\n","")
134     parsedLine = line.split("|")
135     ## Dont add it to the matrix if there is no chg number.
136     if parsedLine[1]:
137     if parsedLine[0].upper() in list(serviceNowToServersMatrix.keys()):
138     serviceNowToServersMatrix[parsedLine[0].upper()].append(line)
139     else:
140     serviceNowToServersMatrix[parsedLine[0].upper()] = [line,]
141     print("There are %s servers on the ServiceNow Export to servers file."%len(list(serviceNowToServersMatrix.keys())))
142 nino.borges 771
143 nino.borges 787 ## Next ingest the Legacy SNOW Export to server list. check that there is a chg number at all.
144     contents = open(legacySnowToServersFile).readlines()
145     contents = contents[1:]
146     for line in contents:
147     line = line.replace("\n","")
148     parsedLine = line.split("|")
149     ## Dont add it to the matrix if there is no chg number.
150     if parsedLine[1]:
151     if parsedLine[0].upper() in list(legacySnowToServersMatrix.keys()):
152     legacySnowToServersMatrix[parsedLine[0].upper()].append(line)
153     else:
154     legacySnowToServersMatrix[parsedLine[0].upper()] = [line,]
155     print("There are %s servers on the Legacy SNOW Export to servers file."%len(list(legacySnowToServersMatrix.keys())))
156    
157    
158 nino.borges 789 ## Next ingest the SN Numbers from the original win2008 file Export to server list. check that there is a chg number at all.
159     contents = open(win2kChngReqToServersFile).readlines()
160     contents = contents[1:]
161     for line in contents:
162     line = line.replace("\n","")
163     parsedLine = line.split("|")
164     ## Dont add it to the matrix if there is no chg number.
165     if parsedLine[1]:
166     if parsedLine[0].upper() in list(win2kChngReqToServersMatrix.keys()):
167     win2kChngReqToServersMatrix[parsedLine[0].upper()].append(line)
168     else:
169     win2kChngReqToServersMatrix[parsedLine[0].upper()] = [line,]
170     print("There are %s servers on the SN Numbers from the original win2008 file Export to servers file."%len(list(win2kChngReqToServersMatrix.keys())))
171    
172    
173 nino.borges 791 ## Next ingest the crawled SQL databasename to server list
174     contents = open(crawledSqlServersListFile).readlines()
175     contents = contents[1:]
176     for line in contents:
177     line = line.replace("\n","")
178     parsedLine = line.split("|")
179     if parsedLine[0].upper() in list(crawledSqlServersMatrix.keys()):
180     crawledSqlServersMatrix[parsedLine[0].upper()].append(line)
181     else:
182     crawledSqlServersMatrix[parsedLine[0].upper()] = [line,]
183     print("There are %s servers on the crawled SQL databases to servers file."%len(list(crawledSqlServersMatrix.keys())))
184 nino.borges 789
185 nino.borges 791
186 nino.borges 793 ## Finally ingest the other domains crawled SQL databasename to server list
187     #otherDomainsSqlServers
188     contents = open(otherDomainsSqlServersListFile).readlines()
189     contents = contents[1:]
190     for line in contents:
191     line = line.replace("\n","")
192     parsedLine = line.split("|")
193     if parsedLine[0].upper() in list(otherDomainsSqlServersMatrix.keys()):
194     otherDomainsSqlServersMatrix[parsedLine[0].upper()].append(line)
195     else:
196     otherDomainsSqlServersMatrix[parsedLine[0].upper()] = [line,]
197     print("There are %s servers on the Other Domains SQL databases to servers file."%len(list(otherDomainsSqlServersMatrix.keys())))
198 nino.borges 791
199    
200 nino.borges 793
201    
202 nino.borges 771 outputFile = open(outputFileName,'w')
203 nino.borges 793 outputFile.write(win2kServersFileHeadders + "|Found on 2022 SQL Server Report?|Found on 2021 SQL Server Report?|Found DatabaseNames Server Report?|DB Names Found on AspectList|Found on Application Inventory?|Applications|App IT Leadership Owner|App IT System Owner|App Executive Leadership Owner|App Business Owner|Found on ServiceNow|ServiceNowChangeReqNumbers|ServiceNow Business Owner|ServiceNowChangeBusiness/IT Approver|ServiceNow Parent Change Req Numbers|Found on LegacySNOW|Legacy SNOW ChangeRequestNumbers|Win2008 Report ChngRequest Found|Win2008 Report ChngRequestNumbers|Win2008 Report SN Business Owner|Win2008 Report SN Business/IT Approver|Win2008 Report SN Parent Change Req Numbers|Found Crawled DatabaseNames Server Report?|DB Names Found on CrawledSQLList|Found OtherDomains DatabaseNames Server Report?|DB Names Found on OtherDomains SQLList\n")
204 nino.borges 771 for i in list(win2kServersFileMatrix.keys()):
205     nl=outputFile.write(win2kServersFileMatrix[i][0])
206    
207     if i in list(sqlServersListFileMatrix.keys()):
208     nl=outputFile.write("|YES")
209     else:
210     nl=outputFile.write("|NO")
211     if i in list(prevSqlServersListFileMatrix.keys()):
212     nl=outputFile.write("|YES")
213     else:
214     nl=outputFile.write("|NO")
215     dbNamesList = ""
216     if i in list(aspectDatabasesToServersMatrix.keys()):
217     nl=outputFile.write("|YES")
218     for dbNameLine in aspectDatabasesToServersMatrix[i]:
219     dbNameLine = dbNameLine.replace("\n","")
220     dbNameLine = dbNameLine.split("|")
221     if dbNamesList:
222     dbNamesList = dbNamesList + "; " +dbNameLine[1]
223     else:
224     dbNamesList = dbNameLine[1]
225     else:
226     nl=outputFile.write("|NO")
227     if dbNamesList:
228     nl=outputFile.write("|" + dbNamesList)
229     else:
230     nl=outputFile.write("|")
231 nino.borges 772 appList = set()
232     itLeadershipOwnerList = set()
233     itSystemOwnerList = set()
234     executiveLeadershipOwnerList = set()
235     businessOwnerList = set()
236     tempOwnerList = ""
237 nino.borges 771 if i in list(appListServersFileMatrix.keys()):
238     nl=outputFile.write("|YES")
239     for appLine in appListServersFileMatrix[i]:
240     appLine = appLine.replace("\n","")
241     appLine = appLine.split("|")
242 nino.borges 772 appList.add(appLine[1]+' [AppID=%s]'%appLine[0])
243     tempOwnerList = appLine[5].replace("#","")
244     tempOwnerList = tempOwnerList.split(";")
245     for o in tempOwnerList:
246     if o.isnumeric():
247     pass
248     else:
249     itLeadershipOwnerList.add(o+' [AppID=%s]'%appLine[0])
250     tempOwnerList = appLine[6].replace("#","")
251     tempOwnerList = tempOwnerList.split(";")
252     for o in tempOwnerList:
253     if o.isnumeric():
254     pass
255     else:
256     itSystemOwnerList.add(o+' [AppID=%s]'%appLine[0])
257     tempOwnerList = appLine[7].replace("#","")
258     tempOwnerList = tempOwnerList.split(";")
259     for o in tempOwnerList:
260     if o.isnumeric():
261     pass
262     else:
263     executiveLeadershipOwnerList.add(o+' [AppID=%s]'%appLine[0])
264     tempOwnerList = appLine[8].replace("#","")
265     tempOwnerList = tempOwnerList.split(";")
266     for o in tempOwnerList:
267     if o.isnumeric():
268     pass
269     else:
270     businessOwnerList.add(o+' [AppID=%s]'%appLine[0])
271     ## if appList:
272     ## appList = appList + '; ' + appLine[1]+' [AppID=%s]'%appLine[0]
273     ## else:
274     ## appList = appLine[1]+' [AppID=%s]'%appLine[0]
275 nino.borges 771 else:
276     nl=outputFile.write("|NO")
277     if appList:
278 nino.borges 772 nl=outputFile.write("|" + "; ".join(appList))
279 nino.borges 771 else:
280     nl=outputFile.write("|")
281 nino.borges 772 if itLeadershipOwnerList:
282     nl=outputFile.write("|" + "; ".join(itLeadershipOwnerList))
283     else:
284     nl=outputFile.write("|")
285     if itSystemOwnerList:
286     nl=outputFile.write("|" + "; ".join(itSystemOwnerList))
287     else:
288     nl=outputFile.write("|")
289     if executiveLeadershipOwnerList:
290     nl=outputFile.write("|" + "; ".join(executiveLeadershipOwnerList))
291     else:
292     nl=outputFile.write("|")
293     if businessOwnerList:
294     nl=outputFile.write("|" + "; ".join(businessOwnerList))
295     else:
296     nl=outputFile.write("|")
297 nino.borges 783
298    
299    
300     chngReqList = ""
301 nino.borges 784 snBusinessOwnerList = set()
302 nino.borges 785 snApproverOwnerList = set()
303 nino.borges 786 snParentChgReqNumbList = set()
304    
305 nino.borges 783 if i in list(serviceNowToServersMatrix.keys()):
306     nl=outputFile.write("|YES")
307     for chngReqLine in serviceNowToServersMatrix[i]:
308     chngReqLine = chngReqLine.replace("\n","")
309     chngReqLine = chngReqLine.split("|")
310     chngReqDt = chngReqLine[2]
311     ## Grab just the date for now but can change to date and time if needed
312     chngReqDt = chngReqDt.split(" ")[0]
313     chngReqNumbWDate = "%s [%s]"%(chngReqLine[1],chngReqDt)
314     if chngReqList:
315     chngReqList = chngReqList + "; " +chngReqNumbWDate
316     else:
317     chngReqList = chngReqNumbWDate
318 nino.borges 784 if chngReqLine[3]:
319 nino.borges 785 if chngReqLine[3] == "None":
320     pass
321     else:
322     snBusinessOwnerList.add("%s [%s]"%(chngReqLine[3],chngReqLine[1]))
323     if chngReqLine[4]:
324     if chngReqLine[4] == "None":
325     pass
326     else:
327     snApproverOwnerList.add("%s [%s]"%(chngReqLine[4],chngReqLine[1]))
328 nino.borges 786 if chngReqLine[5]:
329     if chngReqLine[5] == "None":
330     pass
331     else:
332     snParentChgReqNumbList.add("%s [%s]"%(chngReqLine[5],chngReqLine[1]))
333 nino.borges 784
334 nino.borges 783 else:
335     nl=outputFile.write("|NO")
336     if chngReqList:
337     nl=outputFile.write("|" + chngReqList)
338     else:
339     nl=outputFile.write("|")
340 nino.borges 784 if snBusinessOwnerList:
341     nl=outputFile.write("|" + "; ".join(snBusinessOwnerList))
342     else:
343     nl=outputFile.write("|")
344 nino.borges 785 if snApproverOwnerList:
345     nl=outputFile.write("|" + "; ".join(snApproverOwnerList))
346     else:
347     nl=outputFile.write("|")
348 nino.borges 786 if snParentChgReqNumbList:
349     nl=outputFile.write("|" + "; ".join(snParentChgReqNumbList))
350     else:
351     nl=outputFile.write("|")
352 nino.borges 783
353    
354 nino.borges 787 snowChngReqList = ""
355     if i in list(legacySnowToServersMatrix.keys()):
356     nl=outputFile.write("|YES")
357     for snowChngReqLine in legacySnowToServersMatrix[i]:
358     snowChngReqLine = snowChngReqLine.replace("\n","")
359     snowChngReqLine = snowChngReqLine.split("|")
360     snowChngReqDt = snowChngReqLine[2]
361     ## Grab just the date for now but can change to date and time if needed
362     snowChngReqDt = snowChngReqDt.split(" ")[0]
363     snowChngReqNumbWDate = "%s [%s]"%(snowChngReqLine[1],snowChngReqDt)
364     if snowChngReqList:
365     snowChngReqList = snowChngReqList + "; " +snowChngReqNumbWDate
366     else:
367     snowChngReqList = snowChngReqNumbWDate
368     else:
369     nl=outputFile.write("|NO")
370    
371     if snowChngReqList:
372     nl=outputFile.write("|" + snowChngReqList)
373     else:
374     nl=outputFile.write("|")
375    
376    
377 nino.borges 789
378    
379     chngReqList = ""
380     snBusinessOwnerList = set()
381     snApproverOwnerList = set()
382     snParentChgReqNumbList = set()
383    
384     if i in list(win2kChngReqToServersMatrix.keys()):
385     nl=outputFile.write("|YES")
386     for chngReqLine in win2kChngReqToServersMatrix[i]:
387     chngReqLine = chngReqLine.replace("\n","")
388     chngReqLine = chngReqLine.split("|")
389     chngReqDt = chngReqLine[3]
390     ## Grab just the date for now but can change to date and time if needed
391     chngReqDt = chngReqDt.split(" ")[0]
392     chngReqNumbWDate = "%s [%s]"%(chngReqLine[1],chngReqDt)
393     if chngReqList:
394     chngReqList = chngReqList + "; " +chngReqNumbWDate
395     else:
396     chngReqList = chngReqNumbWDate
397     if chngReqLine[4]:
398     if chngReqLine[4] == "None":
399     pass
400     else:
401     snBusinessOwnerList.add("%s [%s]"%(chngReqLine[4],chngReqLine[1]))
402     if chngReqLine[5]:
403     if chngReqLine[5] == "None":
404     pass
405     else:
406     snApproverOwnerList.add("%s [%s]"%(chngReqLine[5],chngReqLine[1]))
407     if chngReqLine[6]:
408     if chngReqLine[6] == "None":
409     pass
410     else:
411     snParentChgReqNumbList.add("%s [%s]"%(chngReqLine[6],chngReqLine[1]))
412    
413     else:
414     nl=outputFile.write("|NO")
415     if chngReqList:
416     nl=outputFile.write("|" + chngReqList)
417     else:
418     nl=outputFile.write("|")
419     if snBusinessOwnerList:
420     nl=outputFile.write("|" + "; ".join(snBusinessOwnerList))
421     else:
422     nl=outputFile.write("|")
423     if snApproverOwnerList:
424     nl=outputFile.write("|" + "; ".join(snApproverOwnerList))
425     else:
426     nl=outputFile.write("|")
427     if snParentChgReqNumbList:
428     nl=outputFile.write("|" + "; ".join(snParentChgReqNumbList))
429     else:
430     nl=outputFile.write("|")
431    
432    
433 nino.borges 791
434     crawledDbNamesList = ""
435     if i in list(crawledSqlServersMatrix.keys()):
436     nl=outputFile.write("|YES")
437     for dbNameLine in crawledSqlServersMatrix[i]:
438     dbNameLine = dbNameLine.replace("\n","")
439     dbNameLine = dbNameLine.split("|")
440     if crawledDbNamesList:
441     crawledDbNamesList = crawledDbNamesList + "; " +dbNameLine[1]
442     else:
443     crawledDbNamesList = dbNameLine[1]
444     else:
445     nl=outputFile.write("|NO")
446     if crawledDbNamesList:
447     nl=outputFile.write("|" + crawledDbNamesList)
448     else:
449     nl=outputFile.write("|")
450    
451    
452 nino.borges 793
453     otherDomainDbNamesList = ""
454     if i in list(otherDomainsSqlServersMatrix.keys()):
455     nl=outputFile.write("|YES")
456     for dbNameLine in otherDomainsSqlServersMatrix[i]:
457     dbNameLine = dbNameLine.replace("\n","")
458     dbNameLine = dbNameLine.split("|")
459     if otherDomainDbNamesList:
460     otherDomainDbNamesList = otherDomainDbNamesList + "; " +dbNameLine[1]
461     else:
462     otherDomainDbNamesList = dbNameLine[1]
463     else:
464     nl=outputFile.write("|NO")
465     if otherDomainDbNamesList:
466     nl=outputFile.write("|" + otherDomainDbNamesList)
467     else:
468     nl=outputFile.write("|")
469    
470    
471 nino.borges 771 nl = outputFile.write("\n")
472     outputFile.close()