VoIPAnalyzer / call_flow.py
fabioantonini's picture
Upload 5 files
936432e verified
# call_flow.py
def create_call_flow_diagram(calls_by_id):
"""
Given a dictionary of Call objects (keyed by call_id), generate
a textual call flow for each call. We assume each Call has a
'sip_sequence' attribute: a list of dicts with:
{
'timestamp': datetime object,
'src_ip': str,
'dst_ip': str,
'message': str # e.g. "INVITE (Call-ID: abc)"
}
The output will look like a classic ASCII SIP ladder diagram.
Returns a string that concatenates the diagrams for all calls.
"""
if not calls_by_id:
return "No calls found to display."
# We'll combine diagrams for each call into one text output
output = []
for call_id, call_obj in calls_by_id.items():
# Retrieve the SIP sequence in chronological order (by timestamp)
# You must ensure your parser sets call_obj.sip_sequence sorted by time
sip_sequence = getattr(call_obj, 'sip_sequence', [])
sip_sequence = sorted(sip_sequence, key=lambda x: x['timestamp'])
# Get unique participants for a horizontal layout
# We'll just gather distinct IP addresses from the sequence
# and put them left (caller) -> right (callee).
# If you know which is the caller vs. callee, you can fix that order.
participants = sorted(
list({msg['src_ip'] for msg in sip_sequence} | {msg['dst_ip'] for msg in sip_sequence})
)
if len(participants) < 2:
# If we somehow only have one or zero participants, skip
output.append(f"Call-ID {call_id} only has one participant.\n")
continue
# We’ll just place the first IP as “Left” and last IP as “Right” for demonstration
left_participant = participants[0]
right_participant = participants[-1]
# Header for this call
diagram_lines = []
diagram_lines.append(f"Call Flow for Call-ID: {call_id}")
diagram_lines.append(f" {left_participant:<30} {right_participant}")
diagram_lines.append(" -----------------------------------------------------------")
# Each SIP message in chronological order
for msg in sip_sequence:
src = msg['src_ip']
dst = msg['dst_ip']
message = msg['message']
if src == left_participant and dst == right_participant:
# Left -> Right
diagram_lines.append(f" {message:<30} ----------------->")
elif src == right_participant and dst == left_participant:
# Right -> Left
# We want the message on the right side, but in ASCII, we can do:
# Some spaces, then the message, then <----
# For demonstration, we do something simpler:
diagram_lines.append(f" {message}")
diagram_lines.append(" <-------------------------------")
else:
# If the message is between some other IP pair, we can either
# skip or try to align it in the middle. For now, just note it.
# This might happen if there are multiple proxies or servers.
diagram_lines.append(f" [{src} -> {dst}] {message}")
# Example: If we want to artificially place an "RTP ...." line
# you could do a simple check if the call has media streams:
if call_obj.media_streams:
diagram_lines.append(" RTP ...................... RTP ....................")
# Add a blank line after the call’s diagram
output.append("\n".join(diagram_lines) + "\n")
return "\n".join(output)