Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Call flow graph 2 #30

Merged
merged 25 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
34195eb
setting up design
Jordan-Prescott Apr 29, 2024
e380383
Update broadwork_entities.py
Jordan-Prescott Apr 29, 2024
0ba001e
from_dict methods in-progress
Jordan-Prescott Apr 30, 2024
e7766e6
Continuing the from_dict methods
Jordan-Prescott Apr 30, 2024
aa51fda
Continued from_dict
Jordan-Prescott May 1, 2024
0333161
Create __inti__.py
Jordan-Prescott May 2, 2024
d468a3d
rename
Jordan-Prescott May 2, 2024
8d7fce0
added service provider singular
Jordan-Prescott May 2, 2024
3f97868
call_flow now pulling in data and saving to data_store
Jordan-Prescott May 2, 2024
e614c6b
Up to call_flow storing hunt groups
Jordan-Prescott May 2, 2024
5c24b16
users in cc, hg, and tg are now being pulled from user objects
Jordan-Prescott May 3, 2024
a92986b
update setup
Jordan-Prescott May 6, 2024
a73dda7
missing methods needed
Jordan-Prescott May 8, 2024
5ec03b2
Remaining methods added
Jordan-Prescott May 10, 2024
3a5538c
starting to locate number in entities
Jordan-Prescott May 10, 2024
8490051
function can now find number next: aliases and extension
Jordan-Prescott May 10, 2024
587db80
mappings in datastore and pulling forwarding options
Jordan-Prescott May 13, 2024
2f7282a
traversing entities
Jordan-Prescott May 13, 2024
91d0838
call center data now being captured
Jordan-Prescott May 13, 2024
5497ce1
Call center and users are being traversed
Jordan-Prescott May 14, 2024
38e12af
Feature working as first draft
Jordan-Prescott May 15, 2024
bc5befe
Made start easier
Jordan-Prescott May 15, 2024
d07c998
Solution can now handle numbers that leave the system
Jordan-Prescott May 16, 2024
76cdb9b
External number will get default node
Jordan-Prescott May 16, 2024
1fcfb6c
Delete Calls To 3213061544.svg
Jordan-Prescott May 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,5 @@ sandbox.py

# mac
*.DS_Store

os_reports/
call_flow_data-temp/
163 changes: 160 additions & 3 deletions odins_spear/methods/get.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ def session(self):
# ADMINISTRATORS
# ADVICE OF CHARGE
# ALTERNATE NUMBERS

def user_alternate_numbers(self, user_id: str):
"""Fetches a list of a user/ service such as Auto Attendant, Hunt Group, or Call Centres
alternate numebrs.

Args:
user_id (str): Target user/ service_user_id

Returns:
Dict: List of all alternate numbers assigned to the user/ service.
"""

endpoint = f"/users/alternate-numbers?userId={user_id}"

return self.requester.get(endpoint)

# ANSWER CONFIRMATION
# ALTERNATE USER ID
# ANNOUNCEMENTS
Expand Down Expand Up @@ -63,26 +79,142 @@ def group_call_centers(self, service_provider_id: str, group_id: str):

return self.requester.get(endpoint)


def group_call_center(self, service_user_id: str):

endpoint = f"/groups/call-centers?serviceUserId={service_user_id}"

return self.requester.get(endpoint)


def user_call_center(self, user_id: str):

endpoint = f"/users/call-center?userId={user_id}"

return self.requester.get(endpoint)



def group_call_center_bounced_calls(self, service_user_id: str):

endpoint = f"/groups/call-centers/bounced-calls?serviceUserId={service_user_id}"

return self.requester.get(endpoint)


def group_call_center_forced_forwarding(self, service_user_id: str):

endpoint = f"/groups/call-centers/forced-forwarding?serviceUserId={service_user_id}"

return self.requester.get(endpoint)


def group_call_center_overflow(self, service_user_id):

endpoint = f"/groups/call-centers/overflow?serviceUserId={service_user_id}"

return self.requester.get(endpoint)


def group_call_center_stranded_calls(self, service_user_id):

endpoint = f"/groups/call-centers/stranded-calls?serviceUserId={service_user_id}"

return self.requester.get(endpoint)


def group_call_center_stranded_calls_unavailable(self, service_user_id):

endpoint = f"/groups/call-centers/stranded-calls-unavailable?serviceUserId={service_user_id}"

return self.requester.get(endpoint)

# CALL CONTROL
# CALL FORWARDING ALWAYS
# CALL FORWARDING ALWAYS SECONDARY

def user_call_forwarding_always(self, user_id: str):

endpoint = f"/users/call-forwarding-always?userId={user_id}"

return self.requester.get(endpoint)


def bulk_call_forwarding_always(self, service_provider_id: str, group_id: str):

endpoint = f"/users/call-forwarding-always/bulk?serviceProviderId={service_provider_id}&groupId={group_id}"

return self.requester.get(endpoint)


# CALL FORWARDING BUSY

def user_call_forwarding_busy(self, user_id: str):

endpoint = f"/users/call-forwarding-busy?userId={user_id}"

return self.requester.get(endpoint)


def bulk_call_forwarding_busy(self, service_provider_id: str, group_id: str):

endpoint = f"/users/call-forwarding-busy/bulk?serviceProviderId={service_provider_id}&groupId={group_id}"

return self.requester.get(endpoint)


# CALL FORWARDING NO ANSWER

def user_call_forwarding_no_answer(self, user_id: str):

endpoint = f"/users/call-forwarding-no-answer?userId={user_id}"

return self.requester.get(endpoint)


def bulk_call_forwarding_no_answer(self, service_provider_id: str, group_id: str):

endpoint = f"/users/call-forwarding-no-answer/bulk?serviceProviderId={service_provider_id}&groupId={group_id}"

return self.requester.get(endpoint)


# CALL FORWARDING NOT REACHABLE

def user_call_forwarding_not_reachable(self, user_id: str):

endpoint = f"/users/call-forwarding-not-reachable?userId={user_id}"

return self.requester.get(endpoint)


def bulk_call_forwarding_not_reachable(self, service_provider_id: str, group_id: str):

endpoint = f"/users/call-forwarding-not-reachable/bulk?serviceProviderId={service_provider_id}&groupId={group_id}"

return self.requester.get(endpoint)


# CALL FORWARDING SELECTIVE
# CALL FORWARDING SETTINGS

def user_call_forwarding_selective(self, user_id: str):

endpoint = f"/users/call-forwarding-selective?userId={user_id}"

return self.requester.get(endpoint)


def user_call_forwarding_selective_criterias(self, user_id: str):

endpoint = f"/users/call-forwarding-selective/criteria?userId={user_id}"

return self.requester.get(endpoint)


def user_call_forwarding_selective_criteria(self, user_id: str, criteria_name: str):

endpoint = f"/users/call-forwarding-selective/criteria?criteriaName={criteria_name}&userId={user_id}"

return self.requester.get(endpoint)

# CALL NOTIFY
# CALL PARK
# CALL PICKUP
Expand Down Expand Up @@ -533,6 +665,21 @@ def user_report(self, user_id: str):
# ROUTE LIST
# ROUTING PROFILE
# SCHEDULES

def group_schedules(self, service_provider_id: str, group_id: str):

endpoint = f"/groups/schedules?serviceProviderId={service_provider_id}&groupId={group_id}"

return self.requester.get(endpoint)


def group_events(self, service_provider_id: str, group_id: str, name: str, type: str):

endpoint = f"/groups/events?serviceProviderId={service_provider_id}&groupId={group_id}&name={name}&type={type}"

return self.requester.get(endpoint)


# SECURITY CLASSIFICATION
# SELECTIVE CALL ACCEPTANCE
# SELECTIVE CALL REJECTION
Expand All @@ -553,6 +700,16 @@ def service_providers(self, reseller_id=None):
return self.requester.get(endpoint)


def service_provider(self, service_provider_id: str):
"""
Args:
reseller_id (str): Only list the Service Provider IDs within the specified Reseller.
"""

endpoint = f"/service-providers?serviceProviderId={service_provider_id}"

return self.requester.get(endpoint)

# SERVICES

def user_services_assigned(self, user_id: str):
Expand Down
28 changes: 25 additions & 3 deletions odins_spear/reporter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
from . import reports

class Reporter:
''' generates human friendly reports
'''
""" generates human friendly reports.
"""

def __init__(self, api) -> None:
self.api = api
self.api = api


def call_flow(self, service_provider_id: str, group_id: str, number: str, number_type: str,
broadworks_entity_type: str):
"""Generates a graphical flow chart of how a call to a specified number routes
through the broadworks system.

Args:
service_provider_id (str): Service Provider/ Enterprise where group is hosted.
group_id (str): Group ID where target number for call flow is located.
number (str): Target number for call flow.
number_type (str): Type of number, options: "dn": Direct Number, "extension": Extension, "alias": Alias
broadworks_entity_type (str): Broadworks entity type target number is associated with. \
Options: "auto_attendant": Auto Attendant, "call_center": Call Center, "hunt_group": Hunt Group, "user": User
"""

return reports.call_flow.main(self.api, service_provider_id, group_id, number, number_type,
broadworks_entity_type)


5 changes: 5 additions & 0 deletions odins_spear/reports/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
__all__ = [
"call_flow"
]

from .call_flow import main
140 changes: 140 additions & 0 deletions odins_spear/reports/call_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import json

from tqdm import tqdm

from .report_utils.graphviz_module import GraphvizModule
from .report_utils.helpers import find_entity_with_number_type
from .report_utils.parsing import call_flow_module


from odins_spear.store import DataStore
from odins_spear.store import broadwork_entities as bre

def main(api, service_provider_id: str, group_id: str, number: str, number_type: str,
broadworks_entity_type: str):

data_store = DataStore()

print("Start.\n")
print("Fetching Service Provider & Group details.")
# Gather entities
service_provider = bre.ServiceProvider.from_dict(data=api.get.service_provider(service_provider_id))
group = bre.Group.from_dict(service_provider=service_provider, data=api.get.group(service_provider_id, group_id))

data_store.store_objects(service_provider, group)

auto_attendants = api.get.auto_attendants(service_provider_id, group_id)
for aa in tqdm(auto_attendants, desc=f"Fetching all Auto Attendants."):
auto_attendant = bre.AutoAttendant.from_dict(group=group, data=api.get.auto_attendant(aa['serviceUserId']))
data_store.auto_attendants.append(auto_attendant)

print("Fetching all users this may take a couple of minutes, please wait.")
users = api.get.users(service_provider_id, group_id, extended=True)

# Captures users with the forward fucntionality
call_forward_always_users = [
item["user"]["userId"] for item in api.get.bulk_call_forwarding_always(service_provider_id, group_id) if item["service"]["assigned"] \
and item["data"]["isActive"]
]

call_forward_busy_users = [
item["user"]["userId"] for item in api.get.bulk_call_forwarding_busy(service_provider_id, group_id) if item["service"]["assigned"] \
and item["data"]["isActive"]
]

call_forward_no_answer_users = [
item["user"]["userId"] for item in api.get.bulk_call_forwarding_no_answer(service_provider_id, group_id) if item["service"]["assigned"] \
and item["data"]["isActive"]
]

call_forward_not_reachable = [
item["user"]["userId"] for item in api.get.bulk_call_forwarding_not_reachable(service_provider_id, group_id) if item["service"]["assigned"] \
and item["data"]["isActive"]
]

for u in tqdm(users, desc=f"Parsing all Users."):
user = bre.User.from_dict(group=group, data=u)

if user.id in call_forward_always_users:
user.call_forwarding_always = str(api.get.user_call_forwarding_always(user.id)["forwardToPhoneNumber"])
if user.id in call_forward_busy_users:
user.call_forwarding_busy = str(api.get.user_call_forwarding_busy(user.id)["forwardToPhoneNumber"])
if user.id in call_forward_no_answer_users:
user.call_forwarding_no_answer = str(api.get.user_call_forwarding_no_answer(user.id)["forwardToPhoneNumber"])
if user.id in call_forward_not_reachable:
user.call_forwarding_not_reachable = str(api.get.user_call_forwarding_not_reachable(user.id)["forwardToPhoneNumber"])

data_store.users.append(user)


call_centers = api.get.group_call_centers(service_provider_id, group_id)
for cc in tqdm(call_centers, desc=f"Fetching all Call Centers."):
call_center = bre.CallCenter.from_dict(group= group, data= api.get.group_call_center(cc['serviceUserId']))

try:
overflow_settings = api.get.group_call_center_overflow(call_center.service_user_id)
call_center.overflow_calls_action = overflow_settings["action"]
call_center.overflow_calls_transfer_to_phone_number = overflow_settings["transferPhoneNumber"] \
if call_center.overflow_calls_action == "Transfer" else None
except Exception:
call_center.overflow_calls_action = None
call_center.overflow_calls_transfer_to_phone_number = None

try:
stranded_calls_settings = api.get.group_call_center_stranded_calls(call_center.service_user_id)
call_center.stranded_call_unavailable_action = stranded_calls_settings["action"]
call_center.stranded_call_unavailable_transfer_to_phone_number = stranded_calls_settings["transferPhoneNumber"] \
if call_center.stranded_call_unavailable_action == "Transfer" else None
except Exception:
call_center.stranded_call_unavailable_action = None
call_center.stranded_call_unavailable_transfer_to_phone_number = None

try:
stranded_calls_unavailable_settings = api.get.group_call_center_stranded_calls_unavailable(call_center.service_user_id)
call_center.stranded_call_unavailable_action = stranded_calls_unavailable_settings["action"]
call_center.stranded_call_unavailable_transfer_to_phone_number = stranded_calls_unavailable_settings["transferPhoneNumber"] \
if call_center.stranded_call_unavailable_action == "Transfer" else None
except Exception:
call_center.stranded_call_unavailable_action = None
call_center.stranded_call_unavailable_transfer_to_phone_number = None

try:
forced_forwarding_settings = api.get.group_call_center_forced_forwarding(call_center.service_user_id)
call_center.forced_forwarding_enabled = forced_forwarding_settings["enabled"]
call_center.forced_forwarding_forward_to_phone_number = forced_forwarding_settings["forwardToPhoneNumber"] \
if call_center.forced_forwarding_enabled else None
except Exception:
call_center.forced_forwarding_enabled = False
call_center.forced_forwarding_forward_to_phone_number = None

data_store.call_centers.append(call_center)

hunt_groups = api.get.group_hunt_groups(service_provider_id, group_id)
for hg in tqdm(hunt_groups, desc=f"Fetching all Hunt Groups."):
hunt_group = bre.HuntGroup.from_dict(group=group, data= api.get.group_hunt_group(hg['serviceUserId']))
data_store.hunt_groups.append(hunt_group)

# locate number using broadworks_entity_type to zone in on correct location
call_flow_start_node = find_entity_with_number_type(
number,
number_type.lower(),
getattr(data_store, broadworks_entity_type + "s")
)
call_flow_start_node._start_node = True

# Nodes used in the graph
print("Gathering nodes in flow.")
bre_nodes = call_flow_module(call_flow_start_node, data_store)

print("Generating report.")
# build, generate, save graph
graph = GraphvizModule(
"./os_reports/"
)
graph.generate_call_flow_graph(
bre_nodes,
number
)
print("Saving report.")
graph._save_graph(f"Calls To {number}")
print("\nEnd.")
Loading