-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfunc_process_reports.py
473 lines (398 loc) · 16.5 KB
/
func_process_reports.py
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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
#!/usr/bin/env python
__version__ = "20210129"
__author__ = "Decaff_42"
__copyright__ = "2021 by Decaff_42"
__license__ = """Only non-commercial use with attribution is allowed without prior written permission from Decaff_42."""
"""
VERSION HISTORY:
20200306 - Updated release to fix map generation issues
20210129 - Re-formatting IAW PEPs.
"""
# Import standard python modules
import os
from datetime import datetime
import base64
# Import modules from python_ysf_lib
from python_ysf_lib.func_assign_kd import assign_kills_deaths
from python_ysf_lib.func_assign_wep import assign_bullet_record
from python_ysf_lib.func_get_dats import get_dats
from python_ysf_lib.func_calc_fuel_burn import get_fuel_burn_total
from python_ysf_lib.class_kill_record import KILL
from python_ysf_lib.func_generate_flight_map import get_map_data, scale_map_for_report, run_html_mapping_code
from python_ysf_lib.func_export_file import write_txt
def get_report(report, mode, ysf_path, airplanes, ground_objects, kill_credits, bullet_records, explosions):
"""Run the correct report"""
if mode == "MIL":
report = generate_military_report(report, airplanes, ground_objects, kill_credits, bullet_records, explosions)
elif mode == "CIV":
report = generate_civilian_report(report, airplanes, ysf_path)
return report
def generate_civilian_html_report(airplanes, yfs_path, scenery_name, plot_names, plot_folder, ysf_version, flight_date,
csv_report):
"""Extract civilian data and export to html file."""
print("Generating HTML Report...")
yfs_filename = os.path.basename(yfs_path)
report_date = datetime.today().strftime('%d-%b-%Y')
report_title = "{} Civilian Flight Log Map Report".format(scenery_name)
report_path = os.path.join(os.getcwd(), "HTML Reports")
report_filename = "{}_-_{}_-_{}.html".format(yfs_filename, scenery_name, report_date)
# Replace / in report filename
if "/" in report_filename:
report_filename = report_filename.replace("/", "_")
if "\\" in report_filename:
report_filename = report_filename.replace("\\", "_")
map_data = get_map_data(scenery_name)
if len(map_data) < 1:
print("Ending HTML Creation - Map not in Database")
return []
# Start the HTML file
html = ["<!DOCTYPE html>",
"<html>",
"<head>",
"<title>{}</title>".format(report_title),
"<style>",
"td {padding:2px 10;}",
"img {position:absolute; left:0%; top: 0em; height:50em; width: auto;}.details {position:absolute; top:50em;}.container{position: relative;}",
"</style>",
"</head>",
"<body>",
'<div class="header">',
"<h3><center>YSF Flight Log Report</center></h3>",
"<table>",
"<tr>",
'<th align="left">Map: </th>',
"<td>{}</td>".format(scenery_name),
"</tr>",
"<tr>",
'<th align="left">YSF Version: </th>',
"<td>{}</td>".format(ysf_version),
"</tr>",
"<tr>",
'<th align="left">Flown On:</th>',
"<td>{}</td>".format(flight_date),
"</tr>",
"<tr>",
'<th align="left">Report Date:</th>',
"<td>{}</td>".format(report_date),
"</tr>",
"</table>",
"</div>"]
# Determine how many sorties each player flew
usernames = list()
list_of_users = dict()
image_order = list()
username_order = list()
sortie_order = list()
pilot_order = list()
for airplane in airplanes:
user = airplane.username
sorties = airplane.sortie_class_instances_sorted()
if user not in usernames:
usernames.append(user)
for sortie in sorties:
sortie_order.append(sortie)
pilot_order.append(user)
if len(sorties) > 0:
if user not in username_order:
username_order.append(user)
try:
list_of_users[user] += len(sorties)
except KeyError:
list_of_users[user] = len(sorties)
# Sort the sorties and pilots
final_sortie_order = list()
final_pilot_order = list()
max_num_sorties = 0
for user in username_order:
for sortie, pilot in zip(sortie_order, pilot_order):
if pilot == user and sortie.flight_time > 0.016: # Greater than 1 minute of flight time required.
final_sortie_order.append(sortie)
final_pilot_order.append(user)
# Update the list_of_user dictionary
list_of_users[user] = final_pilot_order.count(user)
if list_of_users[user] > max_num_sorties:
max_num_sorties = list_of_users[user]
# Create Plots
image_order = run_html_mapping_code(final_sortie_order, final_pilot_order, scenery_name, yfs_filename)
# Generate checkboxes for each user
script_check_vars = list()
script_img_vars = list()
html.append("<hr>")
html.append("<table>")
total_check_boxes = 0
# Write header row
html.append('<tr style="border-bottom:1px solid black">')
html.append("<th>Pilot</th>")
for i in range(max_num_sorties):
html.append("<th>{}</th>".format(i + 1))
html.append("</tr>")
for user in username_order:
html.append("<tr>")
html.append("<th>{}</th>".format(user))
check_string = ""
for sortie in range(list_of_users[user]):
total_check_boxes += 1
html.append('<td><input type="checkbox" id="chk{}" onclick="showFlight()"></td>'.format(total_check_boxes))
script_check_vars.append(
' var checkBox{} = document.getElementById("chk{}");'.format(total_check_boxes, total_check_boxes))
script_img_vars.append(
' var img{} = document.getElementById("img{}");'.format(total_check_boxes, total_check_boxes))
html.append("</tr>")
html.append("</table>")
# Add show/hide all button
html.append('<button type="button" onclick="showHideAll()">Show All / Hide All Toggle</button>')
html.append("<hr>")
# Encode the images
encoded_images = list()
scale_map_for_report(scenery_name, plot_folder)
scale_plot_name = "Scaled_{}".format(map_data[1])
image_order.insert(0, scale_plot_name)
# Replace / in scenery names
if "/" in scenery_name:
scenery_name = scenery_name.replace("/", "_")
if "\\" in scenery_name:
scenery_name = scenery_name.replace("\\", "_")
for image in image_order:
# Define plot filepath
plot_path = os.path.join(plot_folder, image)
if plot_path.endswith(".png") is False:
plot_path = plot_path + ".png"
# Encode and store images for insertion into HTML file
image_data = base64.b64encode(open(plot_path, 'rb').read()).decode('utf-8')
image_tag = '<img src="data:image/png;base64,{0}"></img>'.format(image_data)
encoded_images.append(image_tag)
# Write the maps
print("Encoded Images: ", len(encoded_images))
html.append('<div class="container">')
html.append('{}'.format(encoded_images[0]))
for ind, image in enumerate(encoded_images[1:]):
html.append('<p id="img{}" style="display:none">{}</p>'.format(ind + 1, image))
# Write the report section
html.append('<div class="details">')
csv_report = csv_report[5:]
html.append('<table border="1">')
for row_num, row in enumerate(csv_report):
html.append('<tr>')
for col in row:
if row_num == 0:
html.append('<th>{}</th>'.format(col))
else:
html.append('<td>{}</td>'.format(col))
html.append('</tr>')
html.append('</table>')
# Finish off HTML file with copyright
html.append("<hr>")
html.append("<center>")
html.append("YSF Flight Log Version 2.2 copyright 2021 by Decaff_42")
html.append("</center>")
html.append("</div>")
html.append("</div>")
# Write the showFlight function
html.append("<script>function showFlight() {")
for i in script_check_vars:
html.append(i)
for i in script_img_vars:
html.append(i)
for i in range(total_check_boxes):
html.append(' if (checkBox{}.checked == true){{'.format(i + 1))
html.append(' img{}.style.display = "block";'.format(i + 1))
html.append(' } else {')
html.append(' img{}.style.display = "none";'.format(i + 1))
html.append(' }')
html.append('}')
# Write showHideAll function
html.append("function showHideAll() {")
for i in script_check_vars:
html.append(i)
for i in script_img_vars:
html.append(i)
html.append('')
html.append(' if (checkBox1.checked == true){')
for ind in range(total_check_boxes):
html.append(' img{}.style.display = "none";'.format(ind + 1))
for ind in range(total_check_boxes):
html.append(' checkBox{}.checked = false;'.format(ind + 1))
html.append(' } else {')
for ind in range(total_check_boxes):
html.append(' img{}.style.display = "block";'.format(ind + 1))
for ind in range(total_check_boxes):
html.append(' checkBox{}.checked = true;'.format(ind + 1))
html.append(' }')
html.append('}')
html.append('</script>')
html.append('</body>')
html.append("</html>")
# Write the HTML report to file
write_txt(os.path.join(report_path, report_filename), html)
print("HTML Report Written")
return image_order
def generate_civilian_report(report, airplanes, ysf_path):
"""Extract Civilian Data from the AIRCRAFT classes and compile a report."""
header = ["AIRCRAFT", "USERNAME", "TAKEOFF TIME", "FLIGHT TIME (HR)",
"FLIGHT DISTANCE (KM)", "FLIGHT DISTANCE (NM)",
"MAX_ALTITUDE (FT)", "AVG SPEED (KT)",
"FUEL BURNED (KG)", "FUEL BURNED (LB)", "DIRECT FLIGHT DISTANCE (NM)"]
report.append(header)
# Get aircraft DAT files.
aircraft_dats = get_dats(ysf_path, airplanes)
output_data = []
# Write output data.
for plane in airplanes:
data = plane.sortie_class_instances_sorted()
identify = plane.aircraft_name
pilot = plane.username
for sortie in data:
# only count sorties that last more than 1 minute
if sortie.flight_time > 0.016:
# Get Fuel Burn numbers for as many aircraft as possible.
if identify in list(aircraft_dats.keys()):
# This has a data file.
dat = aircraft_dats[identify]
fuel = get_fuel_burn_total(dat, sortie.throttle_settings)
fuel1 = int(fuel * 2.20462)
else:
fuel = "N/A"
fuel1 = "N/A"
line = [identify, pilot]
sortie_output_data = sortie.output()
line.extend(sortie_output_data[:-2])
line.append(fuel)
line.append(fuel1)
line.append(sortie_output_data[-2])
output_data.append(line)
output_data.sort(key=lambda x: x[2])
report.extend(output_data)
return report
def generate_military_report(report, airplanes, ground_objects, kill_credits, bullet_records, explosions):
"""Generate military combat report"""
# Assign Kill Credits to airplane and ground classes
airplanes, ground_objects = assign_kills_deaths(airplanes, ground_objects, kill_credits)
print(" Kill and Death data assigned to airplanes.")
# Assign bullet records to airplane classes
airplanes, ground_objects = assign_bullet_record(airplanes, ground_objects, bullet_records)
print(" Bullet Records assigned to Airplanes and Ground Objects")
# Insert YFS Summary
report.extend(yfs_summary(airplanes, ground_objects, kill_credits, bullet_records, explosions))
# Insert Combat Summary
report.extend(combat_summary(airplanes))
# Insert Kill Details
report.extend(kill_details(airplanes, ground_objects, kill_credits))
# Insert Sortie Summaries
report.extend(sortie_stats(airplanes))
return report
def sortie_stats(airplanes):
"""Write a sortie stat section showing weapons fired."""
header = [["----------", "----------"],
["SORTIE STATS"],
["LAUNCH TIME", "USERNAME", "AIRCRAFT", "KILLS", "RESULT",
"GUNS", "AIM9", "AIM9X", "AGM65", "B500", "RKT", "FLR", "AIM120",
"B250", "B500HD", "TANK"]
]
weapon_order = ["GUNS", "AIM9", "AIM9X", "AGM65", "B500", "RKT", "FLR", "AIM120",
"B250", "B500HD", "TANK"]
output = list()
output.append(" ")
output.extend(header)
for plane in airplanes:
launch = plane.start_time()
user = plane.username
air = plane.aircraft_name
kills = len(plane.i_killed_list)
death = plane.result()
line = [launch, user, air, kills, death]
stats = plane.weapon_stats()
for i in weapon_order:
if stats[i] == 0:
line.append("-")
else:
line.append(stats[i])
output.append(line)
return output
def kill_details(airplanes, ground_objects, kill_credits):
"""Provide a list of each kill with who killed whom."""
output = list()
output.append([" "])
header = [["----------", "----------"],
["KILL LOG"],
["TIME", "KILLER NAME", "KILLER VEHICLE", "KILLED NAME",
"KILLED VEHICLE", "WEAPON"]
]
output.extend(header)
# Iterate through kills and get names for killed and killer.
for kill in kill_credits:
kill = KILL(kill)
killer_id = kill.killer
killed_id = kill.killed
kill_time = kill.event_time
kill_wpn = kill.weapon_name
kill_id = int(killer_id[1:])
dead_id = int(killed_id[1:])
# Need default names and ids. Match the vehicle names with unknowns from below.
killer_name = "-"
killer_user = "Unknown"
killed_name = "-"
killed_user = "Unknown"
# lookup killer names & aircraft/gnd objects
if killer_id.startswith("A"):
for plane in airplanes:
if plane.ysf_id == kill_id:
killer_name = plane.aircraft_name
killer_user = plane.username
elif killer_id.startswith("G"):
for gnd in ground_objects:
if kill_id == gnd.id:
killer_user = gnd.gnd_type
killer_name = "-"
# Lookup killed names & aircraft /gnd objects
if killed_id.startswith("A"):
for plane in airplanes:
if plane.ysf_id == dead_id:
killed_name = plane.aircraft_name
killed_user = plane.username
elif killed_id.startswith("G"):
for gnd in ground_objects:
if dead_id == gnd.id:
killed_user = gnd.gnd_type
killed_name = "-"
line = [kill_time, killer_user, killer_name, killed_user,
killed_name, kill_wpn]
output.append(line)
return output
def combat_summary(airplanes):
"""Report the number of kills, deaths and sorties for each user."""
output = list()
output.append(" ")
output.append(["----------", "----------"])
output.append(["USER SUMMARY"])
output.append(["USERNAME", "KILLS", "DEATHS", "SORTIES"])
# Get list of users
user_list = list()
users = dict()
for plane in airplanes:
user = plane.username
if user not in user_list:
user_list.append(user)
users[user] = [0, 0, 0] # Kills/deaths/sorties
# Find the number of deaths and kills they have.
for plane in airplanes:
data = users[plane.username]
data[0] = data[0] + len(plane.i_killed_list)
if plane.result == "KILLED":
data[1] = data[1] + 1
data[2] = data[2] + 1
users[plane.username] = data
for key in users:
line = [key]
line.extend(users[key])
output.append(line)
return output
def yfs_summary(airplanes, ground_objects, kill_credits, bullet_records, explosions):
"""Create a stats of the YFS file"""
output = list()
output.append(["YFS SUMMARY"])
output.append(["SORTIES", len(airplanes)])
output.append(["GROUND OBJECTS", len(ground_objects)])
output.append(["BULLET RECORDS", len(bullet_records)])
output.append(["KILLS RECORDED", len(kill_credits)])
output.append(["EXPLOSIONS RECORDED", len(explosions)])
return output