Skip to content

Commit 2b56803

Browse files
authored
Merge pull request #780 from UWB-Biocomputing/ISSUE-768]-Update-Comments-for-911-Classes
[ISSUE-768] Update comments for 911 input generation code
2 parents 212fb1d + cf545fb commit 2b56803

File tree

2 files changed

+140
-53
lines changed

2 files changed

+140
-53
lines changed

Tools/InputGeneration/ClusterPointProcess/cluster_point_process.py

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# Import necessary libraries
12
import numpy as np
23
import math
34
import lxml.etree as et
@@ -7,7 +8,17 @@
78
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout, QFileDialog, QMessageBox, QDialog, QDialogButtonBox, QGridLayout
89
from cluster_point_process_functions import primprocess, add_types, secprocess, add_vertex_events
910

10-
# Class used for setting type_ratios in the GUI for the cluster point process
11+
# source venv/bin/activate
12+
# python3 cluster_point_process.py
13+
14+
# This script provides a GUI to configure and generate synthetic 911 call data using a cluster point process model.
15+
# It integrates primary and secondary event generation with user-configurable parameters.
16+
17+
18+
# ------------------------------
19+
# Class: TypeRatioDialog
20+
# ------------------------------
21+
# Provides a dialog to input ratios for different 911 call types (Law, EMS, Fire).
1122
class TypeRatioDialog(QDialog):
1223
def __init__(self):
1324
super().__init__()
@@ -17,7 +28,8 @@ def __init__(self):
1728

1829
def init_ui(self):
1930
layout = QVBoxLayout()
20-
31+
32+
# Labels and input fields for call type ratios
2133
self.labels = ["Law", "EMS", "Fire"]
2234
self.entries = {}
2335
for label_text in self.labels:
@@ -28,6 +40,7 @@ def init_ui(self):
2840
layout.addWidget(label)
2941
layout.addWidget(entry)
3042

43+
# OK and Cancel buttons
3144
button_box = QDialogButtonBox(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
3245
button_box.accepted.connect(self.accept)
3346
button_box.rejected.connect(self.on_cancel_clicked)
@@ -37,6 +50,7 @@ def init_ui(self):
3750
self.backup_initial_values() # Save initial values
3851

3952
def accept(self):
53+
"""Validates and processes user inputs when OK is clicked."""
4054
invalid_fields = []
4155
for label, entry in self.entries.items():
4256
text = entry.text().strip()
@@ -51,18 +65,22 @@ def accept(self):
5165
invalid_fields.append(label)
5266

5367
if invalid_fields:
68+
# Show an error message for invalid inputs
5469
error_message = "Invalid or empty values in the following fields:\n"
5570
for field in invalid_fields:
5671
error_message += f"- {field}\n"
5772
QMessageBox.warning(self, "Input Error", error_message)
5873
else:
74+
# Store validated results
5975
self.result = {label: float(entry.text().strip()) for label, entry in self.entries.items()}
6076
super().accept()
6177

6278
def backup_initial_values(self):
79+
"""Stores initial values to restore them if the user cancels."""
6380
self.initial_values = {label: entry.text() for label, entry in self.entries.items()}
6481

6582
def on_cancel_clicked(self):
83+
"""Restores initial values if the dialog is canceled."""
6684
for label, entry in self.entries.items():
6785
current_text = entry.text().strip()
6886
if not current_text: # If the field is empty, reset to initial value
@@ -71,19 +89,25 @@ def on_cancel_clicked(self):
7189

7290
self.reject() # Close the dialog
7391

74-
# Class used for setting prototype values in the GUI for the cluster point process
92+
# ------------------------------
93+
# Class: PrototypesDialog
94+
# ------------------------------
95+
# Allows the user to define prototype configurations for secondary event generation.
7596
class PrototypesDialog(QDialog):
7697
def __init__(self):
7798
super().__init__()
7899
self.setWindowTitle("Set Prototypes")
79100
self.init_ui()
80101

81102
def init_ui(self):
103+
"""Sets up the layout and input fields for prototype configurations."""
82104
layout = QGridLayout()
83105
self.entries = {}
84106

107+
# Labels for prototype parameters
85108
labels = ["mu_r:", "sdev_r:", "mu_intensity:", "sdev_intensity:"]
86109

110+
# Add input fields for four prototypes
87111
for i in range(4):
88112
prototype_label = QLabel(f"Prototype {i}:")
89113
layout.addWidget(prototype_label, i, 0)
@@ -94,6 +118,7 @@ def init_ui(self):
94118
layout.addWidget(QLabel(label), i, j * 2 + 1)
95119
layout.addWidget(entry, i, j * 2 + 2)
96120

121+
# OK and Cancel buttons
97122
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
98123
button_box.accepted.connect(self.accept)
99124
button_box.rejected.connect(self.reject)
@@ -102,6 +127,7 @@ def init_ui(self):
102127
self.setLayout(layout)
103128

104129
def accept(self):
130+
"""Validates and processes user inputs when OK is clicked."""
105131
invalid_fields = []
106132
for label, entry in self.entries.items():
107133
text = entry.text().strip()
@@ -116,6 +142,7 @@ def accept(self):
116142
invalid_fields.append(label)
117143

118144
if invalid_fields:
145+
# Show an error message for invalid inputs
119146
error_message = "Invalid or empty values in the following fields:\n"
120147
for field in invalid_fields:
121148
error_message += f"- {field}\n"
@@ -129,40 +156,43 @@ def __init__(self):
129156
super().__init__()
130157
self.setWindowTitle("911 Call Data Generator")
131158
# Input dialog for type_ratio and prototypes
159+
# These will allow the user to input custom type ratios and prototypes via separate dialogs
132160
self.type_ratio_dialog = None
133161
self.prototypes_dialog = None
134162

135163
# Dictionary for holding type_ratio and prototypes
136-
self.type_ratios = {}
137-
self.prototypes = {}
164+
self.type_ratios = {} # Example: {'Law': 0.64, 'EMS': 0.18, 'Fire': 0.18}
165+
self.prototypes = {} # Example: {0: {'mu_r': 0.0005, 'sdev_r': 0.0001}, ...}
138166

139167
self.init_ui()
140168

141169
def init_ui(self):
142170
layout = QVBoxLayout()
143171

144-
# Graph File input field
172+
# Input field for selecting the graph file (.graphml)
145173
graph_file_label = QLabel("Select Graph File (.graphml):")
146174
self.graph_file_label = QLineEdit()
147175
layout.addWidget(graph_file_label)
148176
layout.addWidget(self.graph_file_label)
149177

178+
# Button to browse and select a graph file
150179
graph_file_button = QPushButton("Browse")
151180
graph_file_button.clicked.connect(self.browse_file)
152181
layout.addWidget(graph_file_button)
153182

154-
# Input labels for GUI
183+
# Input labels and fields for various parameters
184+
# These fields collect user inputs for parameters like event timing, duration, etc.
155185
self.labels = [
156-
"Graph ID:",
157-
"First (seconds):",
158-
"Last (seconds):",
159-
"Mean Time Interval (seconds):",
160-
"Dead Time after Event (seconds):",
161-
"Mean Call Interval after incident (seconds):",
162-
"Mean Duration (seconds):",
163-
"Minimum Duration (seconds):",
164-
"Mean Patience Time (seconds):",
165-
"Mean On-Site Time (seconds):",
186+
"Graph ID:", # Insert graph labels
187+
"First (seconds):", # The time of the first event or call in the dataset, measured in seconds from a reference point (e.g., the start of the logging period).
188+
"Last (seconds):", # The time of the last event or call in the dataset, measured in seconds from the same reference point.
189+
"Mean Time Interval (seconds):", # The average time interval between consecutive 911 calls, measured in seconds.
190+
"Dead Time after Event (seconds):", # The average time period after an event during which no new events or calls are expected to occur, measured in seconds. This could represent a cooldown period or a time when the system is not actively logging new calls.
191+
"Mean Call Interval after incident (seconds):", # The average time interval between the end of an incident and the next 911 call, measured in seconds. This could be used to model the frequency of follow-up calls or related incidents.
192+
"Mean Duration (seconds):", # The average duration of a 911 call or incident, measured in seconds. This includes the time from the start of the call to its conclusion.
193+
"Minimum Duration (seconds):", # The shortest duration of a 911 call or incident in the dataset, measured in seconds. This could be used to filter out very short or incomplete calls.
194+
"Mean Patience Time (seconds):", # The average time a caller is willing to wait on hold before hanging up, measured in seconds. This metric is important for understanding caller behavior and optimizing call center operations.
195+
"Mean On-Site Time (seconds):", # The average time emergency responders spend on-site at an incident, measured in seconds. This includes the time from arrival at the scene to departure.
166196
]
167197

168198
self.entries = {}
@@ -174,24 +204,27 @@ def init_ui(self):
174204
layout.addWidget(label)
175205
layout.addWidget(entry)
176206

177-
# Buttons for opening dialogs (Type Ratio, Prototype)
207+
# Button to open the "Set Type Ratios" dialog
178208
set_type_ratio_button = QPushButton("Set Type Ratios")
179209
set_type_ratio_button.clicked.connect(self.show_type_ratio_dialog)
180210
layout.addWidget(set_type_ratio_button)
181211

212+
# Button to open the "Set Prototypes" dialog
182213
set_prototypes_button = QPushButton("Set Prototypes")
183214
set_prototypes_button.clicked.connect(self.show_prototypes_dialog)
184215
layout.addWidget(set_prototypes_button)
185216

186-
# Submit button
217+
# Button to start the event generation process
187218
generate_button = QPushButton("Generate Events")
188219
generate_button.clicked.connect(self.generate_events)
189220
layout.addWidget(generate_button)
190221

222+
# Set the layout of the UI
191223
self.setLayout(layout)
192224
self.show()
193225

194226
def show_type_ratio_dialog(self):
227+
# Opens a dialog to allow the user to set type ratios
195228
if not self.type_ratio_dialog:
196229
self.type_ratio_dialog = TypeRatioDialog()
197230

@@ -200,14 +233,16 @@ def show_type_ratio_dialog(self):
200233
self.type_ratios = {label: float(entry.text()) for label, entry in self.type_ratio_dialog.entries.items()}
201234

202235
def show_prototypes_dialog(self):
236+
# Opens a dialog to allow the user to set prototypes
203237
if not self.prototypes_dialog:
204238
self.prototypes_dialog = PrototypesDialog()
205239

206240
if self.prototypes_dialog.exec_() == QDialog.Accepted:
241+
# Extract and reformat prototype values
207242
prototypes_entries = self.prototypes_dialog.entries.items()
208243
prototypes_values = {label: float(entry.text()) for label, entry in prototypes_entries}
209244

210-
# Reformatting the prototypes dictionary
245+
# Organize prototype data into nested dictionaries
211246
self.prototypes = {}
212247
for label, value in prototypes_values.items():
213248
split_label = label.split(' - ')
@@ -220,7 +255,9 @@ def show_prototypes_dialog(self):
220255
self.prototypes[prototype_num][var_name] = value
221256

222257
# Function that allows user to browse local files
258+
223259
def browse_file(self):
260+
# Allows the user to browse and select a .graphml file
224261
options = QFileDialog.Options()
225262
options |= QFileDialog.DontUseNativeDialog
226263
file_dialog = QFileDialog()
@@ -242,9 +279,11 @@ def browse_file(self):
242279
# Handles invalid inputs (string instead of int, wrong file
243280
# type but not invalid logic)
244281
def generate_events(self):
282+
# Validates inputs and triggers the event generation process
245283
error_message = ""
246284
invalid_fields = []
247285

286+
# Validate user input fields
248287
for label_text, entry in self.entries.items():
249288
if label_text != "Select Region Grid (.graphml):":
250289
text = entry.text().strip()
@@ -256,27 +295,30 @@ def generate_events(self):
256295
except ValueError:
257296
invalid_fields.append(label_text)
258297

298+
# Check if type ratios and prototypes are set
259299
if not self.type_ratios and not self.prototypes:
260300
error_message += "Please set Type Ratio and Prototype values before generating events."
261301
elif not self.type_ratios:
262302
error_message += "Please set Type Ratio values before generating events."
263303
elif not self.prototypes:
264304
error_message += "Please set Prototype values before generating events."
265305

266-
# Error handling
306+
# Validate the graph file input
267307
graph_file = self.graph_file_label.text().strip()
268308
if not graph_file:
269309
invalid_fields.append("Select Region Grid (.graphml):")
270310
elif not graph_file.endswith(".graphml"):
271311
invalid_fields.append("Select Region Grid (.graphml) must be a .graphml file.")
272312

313+
# Handle errors and display warnings
273314
if invalid_fields:
274315
error_message = "Invalid or empty values in the following fields: (float values only)\n"
275316
for field in invalid_fields:
276317
error_message += f"- {field}\n"
277318

278-
# Event handling
319+
# Proceed with event generation if inputs are valid
279320
try:
321+
# Extract user inputs and use them for event generation
280322
if error_message:
281323
raise ValueError(error_message)
282324

@@ -297,13 +339,16 @@ def generate_events(self):
297339
# PRIMARY EVENTS
298340
###########################################################################
299341
# Start your event generation process here based on the valid inputs
300-
graph = nx.read_graphml('../../gis2graph/graph_files/spd.graphml')
342+
graph_file_path = os.path.join('..', '..', 'gis2graph', 'graph_files', 'spd.graphml')
343+
graph = nx.read_graphml(graph_file_path)
301344
graph_id = str(self.entries["Graph ID:"].text())
302345
graph_attribute = graph.nodes[graph_id]['segments']
303346
graph_grid = np.array(eval(graph_attribute))
304347

348+
349+
305350
# Seed numpy random number to get consistent results
306-
np.random.seed(20)
351+
np.random.seed(20) ## change it when i make the test set to something else
307352

308353
# Call primprocess using the inputs from the interface
309354
incidents = primprocess(first, last, mu, pp_dead_t, graph_grid)

0 commit comments

Comments
 (0)