cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
209
Views
0
Helpful
2
Replies

Finesse API for team config 12.6.2

jason.mcdonald
Level 1
Level 1

Hello,

I am facing an issue where I'm attempting to utilize the put request to update a team specific layout using python. Here is the python code (scrubbed for anything personal):

import requests
from requests.auth import HTTPBasicAuth
import ssl
import os
import re

# Credentials and host configuration
fin_username = 'admin'
fin_password = 'password'

# Base URL for Finesse API
base_url = "https://finesse.cisco.api/finesse/api"

# Create a custom SSL context to avoid compatibility issues
ssl_context = ssl.create_default_context()
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2

# Disable SSL warnings (not recommended for production use)
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)


# Function to update layout config for a specific team using PUT request
def update_team_layout_config():
try:
print("Starting update_team_layout_config...") # Debugging print statement
# Check if input file exists
file_path = 'updatedlayout.xml'
if not os.path.exists(file_path):
raise FileNotFoundError(f"Input file '{file_path}' not found.")

# Load data from input file
with open(file_path, 'r') as file:
data = file.read()

# Use regex to extract team ID and layout XML
team_match = re.search(r'"team":\s*(\d+)', data)
layout_match = re.search(r'"layoutxml":\s*(<.*)', data, re.DOTALL)

if not team_match:
raise ValueError("Missing 'team' key in the input file.")
if not layout_match:
raise ValueError("Missing 'layoutxml' key in the input file.")

team_id = team_match.group(1).strip()
layout_xml = layout_match.group(1).strip()

# Ensure the layout XML starts with a valid XML element
if not layout_xml.startswith("<"):
raise ValueError("Invalid XML format in 'layoutxml'.")

# Fix the XML by ensuring that quotes are properly placed around attributes
layout_xml = re.sub(r'(xmlns=)(\S+)', r'\1"\2"', layout_xml)

# Construct the PUT URL dynamically based on team ID
put_url = f"{base_url}/Team/{team_id}/LayoutConfig"

# Set up headers and payload
headers = {
'Content-Type': 'application/xml'
}
payload = f"""<TeamLayoutConfig>
<useDefault>false</useDefault>
<layoutxml>{layout_xml}
</layoutxml>
</TeamLayoutConfig>"""

# Print payload for debugging purposes
print("Payload being sent:")
print(payload)

# Perform PUT request
response = requests.put(put_url, auth=HTTPBasicAuth(fin_username, fin_password), headers=headers, data=payload,
verify=False)

# Handle response
if response.status_code == 200:
print("Successfully updated team layout config:")
print(response.text)
else:
print(f"Failed to update layout config: {response.status_code} {response.text}")

# Print response for debugging purposes
print("Response status code:", response.status_code)
print("Response headers:", response.headers)
print("Response text:", response.text)
except Exception as e:
print(f"An error occurred: {str(e)}")


# Main function to update layout config
def main():
print("Starting main function...") # Debugging print statement
update_team_layout_config()


if __name__ == "__main__":
main()


here is the xml file Im using:

"team": 5000
"layoutxml": <!-- \r\n*Note: When you upgrade, modify Custom Layout XML appropriately to utilize the benefits of new gadgets.\r\n-->\r\n
<finesseLayout xmlns="http://www.cisco.com/vtg/finesse">
    <layout>
        <role>Agent</role>
        <page>
            <gadget>/desktop/scripts/js/callcontrol.js</gadget>
        </page>
        <tabs>
            <tab>
                <id>home</id>
                <icon>home</icon>
                <label>finesse.container.tabs.agent.homeLabel</label>
                <columns>
                    <column>
                        <gadgets>
                        <
                            
                            <!-- The following gadget is for CloudCherry Customer Experience Journey. 
                            If CloudCherry is onboarded successfully with all configurations, then replace the url 
                            with the actual url obtained by exporting the Cisco Finesse gadget from CloudCherry -->
<!-- <gadget>/3rdpartygadget/files/CXService/CiscoCXJourneyGadget.xml</gadget> -->
 
                            <gadget>/desktop/scripts/js/queueStatistics.js</gadget>
 
                            <!--
                The following Gadgets are for LiveData.
                If you wish to show LiveData Reports, then do the following:
                    1) Uncomment each Gadget you wish to show.
                    2) Replace all instances of "my-cuic-server.com" with the Fully Qualified Domain Name of your Intelligence Center Server.
                    3) [OPTIONAL] Adjust the height of the gadget by changing the "gadgetHeight" parameter.
                IMPORTANT NOTES:
                    - In order for these Gadgets to work, you must have performed all documented pre-requisite steps.
                    - Do *NOT* change the viewId (unless you have built a custom report and know what you are doing).
                    - The "teamName" will be automatically replaced with the Team Name of the User logged into Finesse (for Team-specific layouts).
-->
                            <!-- HTTPS Version of LiveData Gadgets -->
                            <!-- TEAM STATUS REPORTS: 1. Agent Default view (default), 2. Agent Skill Group Default view -->
                                    viewId_1=99E6C8E210000141000000D80A0006C4&amp;filterId_1=agent.id=CL%20teamName&amp;
                                    viewId_2=9AB7848B10000141000001C50A0006C4&amp;filterId_2=agent.id=CL%20teamName</gadget> -->
                            <!-- QUEUE STATUS REPORTS: 1. Skill Group Default view (default), 2. Skill Group Utilization view, 3. Precision Queue Default view, 4. Precision Queue Utilization view -->
                            <!-- <gadget>https://my-cuic-server.com:8444/cuic/gadget/LiveData/LiveDataGadget.jsp?
gadgetHeight=310&amp;viewId_1=B7371BE210000144000002870A0007C5&amp;filterId_1=skillGroup.id=CL%20teamName
&amp;viewId_2=9E760C8B1000014B0000005A0A0006C4&amp;filterId_2=skillGroup.id=CL%20teamName
&amp;viewId_3=B71A630C10000144000002480A0007C5&amp;filterId_3=precisionQueue.id=CL%20teamName&amp;
viewId_4=286B86F01000014C000005330A0006C4&amp;filterId_4=precisionQueue.id=CL%20teamName</gadget> -->
                        </gadgets>
                    </column>
                </columns>
            </tab>
            <tab>
                <id>myStatistics</id>
                <icon>column-chart</icon>
                <label>finesse.container.tabs.agent.myStatisticsLabel</label>
                <columns>
                    <column>
                        <gadgets>
                            <gadget>https://my-cuic-server.com:8444/cuic/gadget/LiveData/LiveDataGadget.jsp?
gadgetHeight=150&amp;viewId=0B8D11317ED54A80B64F3AE28C5139E5&amp;
filterId=agentStats.id=CL%20teamName</gadget>
                        </gadgets>
                    </column>
                </columns>
            </tab>
            <tab>
                <id>myHistory</id>
                <icon>history</icon>
                <label>finesse.container.tabs.agent.myHistoryLabel</label>
                <columns>
                    <column>
                        <!-- The following gadgets are used for viewing the call history and state history of an agent. -->
                        <gadgets>
                            <gadget>https://my-cuic-server.com:8444/cuic/gadget/LiveData/LiveDataGadget.jsp?
gadgetHeight=280&amp;viewId=5FA44C6F930C4A64A6775B21A17EED6A&amp;
filterId=agentTaskLog.id=CL%20teamName</gadget>
                            <gadget>https://my-cuic-server.com:8444/cuic/gadget/LiveData/LiveDataGadget.jsp?
gadgetHeight=280&amp;viewId=56BC5CCE8C37467EA4D4EFA8371258BC&amp;
filterId=agentStateLog.id=CL%20teamName</gadget>
                        </gadgets>
                    </column>
                </columns>
            </tab>
            <!--
The following Gadgets are for LiveData.
If you wish to show More LiveData Reports, then do the following:
    1) Uncomment each Gadget you wish to show.
    2) Replace all instances of "my-cuic-server.com" with the Fully Qualified Domain Name of your Intelligence Center Server.
    3) [OPTIONAL] Adjust the height of the gadget by changing the "gadgetHeight" parameter.
IMPORTANT NOTES:
    - In order for these Gadgets to work, you must have performed all documented pre-requisite steps.
    - Do *NOT* change the viewId (unless you have built a custom report and know what you are doing).
    - The "teamName" will be automatically replaced with the Team Name of the User logged into Finesse (for Team-specific layouts).
-->
            <!-- If you are showing the "More Live Data Reports" tab, then also uncomment this section.
            <tab>
                <id>moreLiveDataReports</id>
                <icon>reports-more</icon>
                <label>finesse.container.tabs.agent.moreLiveDataReportsLabel</label>
                <gadgets>
-->
            <!-- HTTPS Version of LiveData Gadgets -->
            <!-- AGENT REPORTS: 1. Agent Default view (default) -->
viewId_1=99E6C8E210000141000000D80A0006C4&amp;filterId_1=agent.id=CL%20teamName</gadget>-->
<!-- AGENT SKILL GROUP REPORTS: 1. Agent Skill Group Default view (default) -->
viewId_1=9AB7848B10000141000001C50A0006C4&amp;filterId_1=agent.id=CL%20teamName</gadget>-->
            <!-- QUEUE STATUS SKILL GROUP REPORTS: 1. Skill Group Default view (default), 2. Skill Group Utilization view -->
viewId_1=B7371BE210000144000002870A0007C5&amp;filterId_1=skillGroup.id=CL%20teamName&amp;
viewId_2=9E760C8B1000014B0000005A0A0006C4&amp;filterId_2=skillGroup.id=CL%20teamName</gadget>-->
            <!-- QUEUE STATUS PRECISION QUEUE REPORTS: 1. Precision Queue Default view (default), 2. Precision Queue Utilization view -->
viewId_1=B71A630C10000144000002480A0007C5&amp;filterId_1=precisionQueue.id=CL%20teamName&amp;
viewId_2=286B86F01000014C000005330A0006C4&amp;filterId_2=precisionQueue.id=CL%20teamName</gadget>-->
            <!-- If you are showing the "more reports" tab, then uncomment this section too.
                </gadgets>
            </tab>
            -->
        </tabs>
    </layout>
    <layout>
        <role>Supervisor</role>
        <page>
            <gadget>/desktop/scripts/js/callcontrol.js</gadget>
        </page>
        <tabs>
            <tab>
                <id>home</id>
                <icon>home</icon>
                <label>finesse.container.tabs.supervisor.homeLabel</label>
                <columns>
                    <column>
                        <gadgets>
                        <!-- The following gadget is for CloudCherry Customer Experience Analytics. 
                            If CloudCherry is onboarded successfully with all configurations, then replace the url 
                            with the actual url obtained by exporting the Cisco Finesse gadget from CloudCherry -->
<!-- <gadget>/3rdpartygadget/files/CXService/CiscoCXAnalyticsGadget.xml</gadget> -->
 
                            <gadget id="team-performance">/desktop/scripts/js/teamPerformance.js</gadget>
                            <!-- The following gadgets are used for viewing the call history and state history of an agent selected in the Team Performance Gadget. -->
                            <!-- The following gadgets are managed(loaded and displayed) by the team performance gadget (associated with id "team-performance").
                                 This association is done using the mapping of managedBy attribute of the managed gadgets, to the id of managing gadget.
                                 If the id for team performance gadget is changed, the values for the associated managedBy attribute
                                 for the managed gadgets, also need to be updated with the new id.
 
                                 These managed gadgets are not displayed by default, but would be displayed when the option 
                                 "view history" is selected, for an agent, in the team performance gadget.
 
                                 Note: As managed gadgets are not displayed by default, placing managed gadgets alone on
                                 separate columns of their own, would display blank space in that area.
                                 For more details on managed gadgets and managedBy attribute, please refer to Finesse Administration Guide. 
                            -->
gadgetHeight=275&amp;viewId=630CB4C96B0045D9BFF295A49A0BA45E&amp;filterId=agentTaskLog.id=AgentEvent:Id
&amp;type=dynamic&amp;maxRows=20</gadget>
gadgetHeight=275&amp;viewId=56BC5CCE8C37467EA4D4EFA8371258BC&amp;filterId=agentStateLog.id=AgentEvent:Id
&amp;type=dynamic&amp;maxRows=20</gadget>
                        </gadgets>
                    </column>
                </columns>
            </tab>
            <tab>
                <id>myHistory</id>
                <icon>history</icon>
                <label>finesse.container.tabs.agent.myHistoryLabel</label>
                <columns>
                    <column>
                        <!-- The following gadgets are used for viewing the call history and state history of a logged in supervisor. -->
                        <gadgets>
viewId=5FA44C6F930C4A64A6775B21A17EED6A&amp;filterId=agentTaskLog.id=CL%20teamName</gadget>
viewId=56BC5CCE8C37467EA4D4EFA8371258BC&amp;filterId=agentStateLog.id=CL%20teamName</gadget>
                        </gadgets>
                    </column>
                </columns>
            </tab>
            <tab>
                <id>teamData</id>
                <icon>team-data</icon>
                <label>finesse.container.tabs.supervisor.teamDataLabel</label>
                <columns>
                    <column>
                        <!-- The following gadget is used by the supervisor to view an agent's queue interval details. -->
                        <gadgets>
viewId=0B8D11317ED54A80B64F3AE28C5139E5&amp;filterId=agentStats.id=CL%20teamName</gadget>
                            <gadget>https://my-cuic-server.com:8444/cuic/gadget/Historical/HistoricalGadget.jsp?
viewId=BD9A8B7DBE714E7EB758A9D472F0E7DC&amp;linkType=htmlType&amp;viewType=Grid
&amp;refreshRate=900&amp;@start_date=RELDATE%20THISWEEK&amp;@end_date=RELDATE%20THISWEEK&amp;
@agent_list=CL%20~teams~&amp;gadgetHeight=360</gadget>
                        </gadgets>
                    </column>
                </columns>
            </tab>
            <tab>
                <id>queueData</id>
                <icon>storage</icon>
                <label>finesse.container.tabs.supervisor.queueDataLabel</label>
                <columns>
                    <column>
                        <gadgets>
                            <gadget>/desktop/scripts/js/queueStatistics.js</gadget>
                        </gadgets>
                    </column>
                </columns>
            </tab>
        </tabs>
    </layout>
    </finesseLayout>

When I run that code, using that xml file this is what I'm getting:
Failed to update layout config: 400 <ApiErrors>
<ApiError>
<ErrorType>Invalid Input</ErrorType>
<ErrorData>TeamLayoutConfig</ErrorData>
<ErrorMessage>Element type &quot;finesseLayout&quot; must be followed by either attribute specifications, &quot;&amp;gt;&quot; or &quot;/&amp;gt;&quot;.</ErrorMessage>
</ApiError>
</ApiErrors>
 
 
Has anyone had success using the API to update the team layout?

Regards,
Jason McDonald
1 Accepted Solution

Accepted Solutions

jason.mcdonald
Level 1
Level 1

Found a couple issues, one with the script which is fixed below:

import requests
from requests.auth import HTTPBasicAuth
import ssl
import os
import re

# Credentials and host configuration
fin_username = 'AdminAccount'
fin_password = 'AdminPassword'

# Base URL for Finesse API
base_url = "https://<finesse ip or hostname>/finesse/api"

# Create a custom SSL context to avoid compatibility issues
ssl_context = ssl.create_default_context()
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2

# Disable SSL warnings (not recommended for production use)
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)


# Function to update layout config for a specific team using PUT request
def update_team_layout_config():
try:
print("Starting update_team_layout_config...") # Debugging print statement
# Check if input file exists
file_path = '<full path to>updatedlayout.xml'
if not os.path.exists(file_path):
raise FileNotFoundError(f"Input file '{file_path}' not found.")

# Load data from input file
with open(file_path, 'r') as file:
data = file.read()

# Use regex to extract team ID and layout XML
team_match = re.search(r'"team":\s*(\d+)', data)
layout_match = re.search(r'"layoutxml":\s*(<.*)', data, re.DOTALL)

if not team_match:
raise ValueError("Missing 'team' key in the input file.")
if not layout_match:
raise ValueError("Missing 'layoutxml' key in the input file.")

team_id = team_match.group(1).strip()
layout_xml = layout_match.group(1).strip()

# Ensure the layout XML starts with a valid XML element
if not layout_xml.startswith("<"):
raise ValueError("Invalid XML format in 'layoutxml'.")

# Fix the XML by ensuring that quotes are properly placed around attributes
layout_xml = re.sub(r'(xmlns=)"?([^\s>]+)"?', r'\1"\2"', layout_xml)

# Remove any extra quotes that may have been added incorrectly
layout_xml = re.sub(r'""', r'"', layout_xml)

# Construct the PUT URL dynamically based on team ID
put_url = f"{base_url}/Team/{team_id}/LayoutConfig"

# Set up headers and payload
headers = {
'Content-Type': 'application/xml'
}
payload = f"""<TeamLayoutConfig>
<useDefault>false</useDefault>
<layoutxml><![CDATA[{layout_xml}]]></layoutxml>
</TeamLayoutConfig>"""

# Print payload for debugging purposes
print("Payload being sent:")
print(payload)

# Perform PUT request
response = requests.put(put_url, auth=HTTPBasicAuth(fin_username, fin_password), headers=headers, data=payload,
verify=False)

# Handle response
if response.status_code == 200:
print("Successfully updated team layout config:")
print(response.text)
else:
print(f"Failed to update layout config: {response.status_code} {response.text}")

# Print response for debugging purposes
print("Response status code:", response.status_code)
print("Response headers:", response.headers)
print("Response text:", response.text)
except Exception as e:
print(f"An error occurred: {str(e)}")


# Main function to update layout config
def main():
print("Starting main function...") # Debugging print statement
update_team_layout_config()


if __name__ == "__main__":
main()

 

the second was the layoutxml file was missing things the team update Api page didn't say were required, those items were:

the configs, header, header columns and version sections are all required. The API page says this is all that's required but that's incorrect:

<TeamLayoutConfig>
<useDefault>false</useDefault>
<layoutxml>
<finesseLayout xmlns="http://www.cisco.com/vtg/finesse">
<layout>
<role>Agent</role>
...
</layout>
<layout>
<role>Supervisor</role>
...
</layout>
</finesseLayout>
</layoutxml>
</TeamLayoutConfig>

If you were to send only the above, you would receive a 400 error code saying the above sections were missing (I added them one at a time to see if all were required and they are). Once all of those were added with the appropriate content AND I fixed some of the URLs that had whitespace in them I'm happy to report this is now working smoothly.

Hope this helps someone in the future to avoid the pain I just endured. Happy coding.

Jason

View solution in original post

2 Replies 2

I don't know if this is your issue, but I had problem doing API updates in PCCE if the update XML did not include the refURL and the changeStamp of the item being updated. changeStamp is used for concurrency to prevent a last update wins scenario.

jason.mcdonald
Level 1
Level 1

Found a couple issues, one with the script which is fixed below:

import requests
from requests.auth import HTTPBasicAuth
import ssl
import os
import re

# Credentials and host configuration
fin_username = 'AdminAccount'
fin_password = 'AdminPassword'

# Base URL for Finesse API
base_url = "https://<finesse ip or hostname>/finesse/api"

# Create a custom SSL context to avoid compatibility issues
ssl_context = ssl.create_default_context()
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2

# Disable SSL warnings (not recommended for production use)
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)


# Function to update layout config for a specific team using PUT request
def update_team_layout_config():
try:
print("Starting update_team_layout_config...") # Debugging print statement
# Check if input file exists
file_path = '<full path to>updatedlayout.xml'
if not os.path.exists(file_path):
raise FileNotFoundError(f"Input file '{file_path}' not found.")

# Load data from input file
with open(file_path, 'r') as file:
data = file.read()

# Use regex to extract team ID and layout XML
team_match = re.search(r'"team":\s*(\d+)', data)
layout_match = re.search(r'"layoutxml":\s*(<.*)', data, re.DOTALL)

if not team_match:
raise ValueError("Missing 'team' key in the input file.")
if not layout_match:
raise ValueError("Missing 'layoutxml' key in the input file.")

team_id = team_match.group(1).strip()
layout_xml = layout_match.group(1).strip()

# Ensure the layout XML starts with a valid XML element
if not layout_xml.startswith("<"):
raise ValueError("Invalid XML format in 'layoutxml'.")

# Fix the XML by ensuring that quotes are properly placed around attributes
layout_xml = re.sub(r'(xmlns=)"?([^\s>]+)"?', r'\1"\2"', layout_xml)

# Remove any extra quotes that may have been added incorrectly
layout_xml = re.sub(r'""', r'"', layout_xml)

# Construct the PUT URL dynamically based on team ID
put_url = f"{base_url}/Team/{team_id}/LayoutConfig"

# Set up headers and payload
headers = {
'Content-Type': 'application/xml'
}
payload = f"""<TeamLayoutConfig>
<useDefault>false</useDefault>
<layoutxml><![CDATA[{layout_xml}]]></layoutxml>
</TeamLayoutConfig>"""

# Print payload for debugging purposes
print("Payload being sent:")
print(payload)

# Perform PUT request
response = requests.put(put_url, auth=HTTPBasicAuth(fin_username, fin_password), headers=headers, data=payload,
verify=False)

# Handle response
if response.status_code == 200:
print("Successfully updated team layout config:")
print(response.text)
else:
print(f"Failed to update layout config: {response.status_code} {response.text}")

# Print response for debugging purposes
print("Response status code:", response.status_code)
print("Response headers:", response.headers)
print("Response text:", response.text)
except Exception as e:
print(f"An error occurred: {str(e)}")


# Main function to update layout config
def main():
print("Starting main function...") # Debugging print statement
update_team_layout_config()


if __name__ == "__main__":
main()

 

the second was the layoutxml file was missing things the team update Api page didn't say were required, those items were:

the configs, header, header columns and version sections are all required. The API page says this is all that's required but that's incorrect:

<TeamLayoutConfig>
<useDefault>false</useDefault>
<layoutxml>
<finesseLayout xmlns="http://www.cisco.com/vtg/finesse">
<layout>
<role>Agent</role>
...
</layout>
<layout>
<role>Supervisor</role>
...
</layout>
</finesseLayout>
</layoutxml>
</TeamLayoutConfig>

If you were to send only the above, you would receive a 400 error code saying the above sections were missing (I added them one at a time to see if all were required and they are). Once all of those were added with the appropriate content AND I fixed some of the URLs that had whitespace in them I'm happy to report this is now working smoothly.

Hope this helps someone in the future to avoid the pain I just endured. Happy coding.

Jason