Búsquedas recientes


No hay búsquedas recientes

Salim Moumouni's Avatar

Salim Moumouni

Incorporación 02 feb 2022

·

Última actividad 28 dic 2023

Seguimientos

0

Seguidores

0

Actividad total

60

Votos

4

Suscripciones

18

RESUMEN DE LA ACTIVIDAD

Última actividad de Salim Moumouni

Salim Moumouni creó una publicación,

Publicación Developer - Zendesk APIs

Hello,

I am currently attempting to export all the tickets within our zendesk instance for the month of September using zendesks api however im running into inconsistencies with the results vs what the number of tickets should be.

Looking at Zendesk explore, for the month of September, our ticket count should be in the 4-5 thousand mark, When I run the script however, Im only getting back 400-500 tickets.I've spot checked tickets that are missing from the results and they fit the conditions needed to appear in the result.

Currently the conditions I have set are:

1. Exclude tickets under certain brand Ids
2. Exclude tickets that have the tag bp_call or spam

See the script below. Any ideas on why there is such a huge discreptancy?

import requests
import json
from datetime import datetime, timedelta

# Constants
BASE_URL = "https://.zendesk.com/api/v2"
ENDPOINT = "/incremental/tickets/cursor"
AUTH = ('', '')

EXCLUDED_BRAND_IDS = {
  360004255072: "Printer Setup",
  360000044983: "DiveR,,,",
  360000046986: "freediving",
  360000041126: "Freediving",
  1900000261367: "Massachusetts...",
  4562467897108: "Mass...",
  7689048: "Nevada ",
  360003769091: "New York ",
  4417994256788: "New York Vend...",
  5046048: "Pennsylvania...",
  360002459491: "Phone System Event",
  360000017906: "R",
  360002324671: "R support...",
  360000041106: "Scuba",
  360002436671: "Diver....",
  360002480172: "Diver sales...",
  360002781632: "Diver io...
}

def get_tickets_for_month(year, month):
  """Retrieve tickets for a specific month."""
  start_date = datetime(year, month, 1)
  if month == 12:
      end_date = datetime(year + 1, 1, 1)
  else:
        end_date = datetime(year, month + 1, 1)

  start_time = int((start_date - datetime(1970, 1, 1)).total_seconds())
    end_time = int((end_date - datetime(1970, 1, 1)).total_seconds())

  params = {
      'start_time': start_time,
      'end_time': end_time
    }

  tickets = []
  current_endpoint = ENDPOINT
    page_count = 0

  while True:
      response = requests.get(BASE_URL + current_endpoint, params=params, auth=AUTH)
      response.raise_for_status()
      data = response.json()

      page_count += 1
      print(f"Processing page {page_count}...")

      # Exclude tickets with certain tags and brand IDs
      new_tickets = [
          ticket for ticket in data.get('tickets', [])
          if "bp_call" not in ticket["tags"] and "spam" not in ticket["tags"] and ticket.get("brand_id") not in EXCLUDED_BRAND_IDS
        ]

        tickets.extend(new_tickets)

      print(f"Current page has {len(data.get('tickets', []))} tickets. After exclusions: {len(new_tickets)} tickets.")
        print(f"Next page: {data.get('next_page')}")

      # If there's a next page URL provided, use it. Else, break out of the loop.
      if data.get("next_page"):
          params = {}  # Clear the existing parameters
          current_endpoint = data["next_page"].split(BASE_URL)[1]
      else:
            break

  print(f"Total pages processed: {page_count}")
    return tickets

def get_ticket_comments(ticket_id):
  COMMENT_ENDPOINT = f"/tickets/{ticket_id}/comments.json"
  response = requests.get(BASE_URL + COMMENT_ENDPOINT, auth=AUTH)
  response.raise_for_status()
    return response.json().get('comments', [])

def get_user_details(user_id):
  USER_ENDPOINT = f"/users/{user_id}.json"
  response = requests.get(BASE_URL + USER_ENDPOINT, auth=AUTH)
  response.raise_for_status()
    return response.json().get('user', {})

def get_ticket_field_definitions():
  TICKET_FIELDS_ENDPOINT = "/ticket_fields.json"
  response = requests.get(BASE_URL + TICKET_FIELDS_ENDPOINT, auth=AUTH)
  response.raise_for_status()
    return {field['id']: field['title'] for field in response.json().get('ticket_fields', [])}

def get_brand_details(brand_id):
  """Fetch the brand name using the brand ID."""
  BRAND_ENDPOINT = f"/brands/{brand_id}.json"
  response = requests.get(BASE_URL + BRAND_ENDPOINT, auth=AUTH)
  response.raise_for_status()
    return response.json().get('brand', {}).get('name', '')

def get_ticket_metrics(ticket_id):
  """Fetch the ticket metrics using the ticket ID."""
  METRICS_ENDPOINT = f"/tickets/{ticket_id}/metrics.json"
  response = requests.get(BASE_URL + METRICS_ENDPOINT, auth=AUTH)
  response.raise_for_status()
    return response.json().get('ticket_metric', {})

def transform_tickets(tickets, ticket_field_definitions):
  print("Starting data transformation...")
    transformed_data = []

  for ticket in tickets:
      requester_details = get_user_details(ticket["requester_id"])
      assignee_details = get_user_details(ticket["assignee_id"]) if ticket["assignee_id"] else {}
      brand_name = get_brand_details(ticket.get("brand_id")) if ticket.get("brand_id") else None
        ticket_metrics = get_ticket_metrics(ticket["id"])

        # Filter out custom fields with null values

      custom_fields_data = {
          ticket_field_definitions.get(field['id'], str(field['id'])): field['value']
          for field in ticket.get('custom_fields', []) if field['value'] is not None
      }

      item = {
          "ID": ticket["id"],
          "Assignee ID": ticket["assignee_id"],
          "Assignee Email": assignee_details.get("email", ""),
          "Brand ID": ticket.get("brand_id", ""),
          "Brand Name": brand_name,  # Include the brand name
          "Recipient": ticket["recipient"],
          "URL": ticket["url"],
          "Conversation Item Timestamp": ticket["created_at"],
          "Conversation Item Title": ticket["subject"],
          "Conversation Item Body": {
              "Tags": ",".join(ticket["tags"]),
              "HTML Body": ticket.get("description", "")
          },
          "Conversation Item Source": "Zendesk",
          "Customer Email": requester_details.get("email", ""),
          "Customer Name": requester_details.get("name", ""),
          "Customer Phone": requester_details.get("phone", ""),
          "Solved At": ticket_metrics.get("solved_at", None),  
          "Custom Fields": custom_fields_data
      }
      comments = get_ticket_comments(ticket["id"])
      item["Comments"] = [comment['body'] for comment in comments]
      transformed_data.append(item)
  print(f"Data transformation complete for {len(transformed_data)} items.")
    return transformed_data

def save_data(data, year, month):
  path = f"C:/users//downloads/zendesk_data_{year}_{month}.json"
  print(f"Saving data to {path}...")
  with open(path, 'w') as outfile:
      json.dump(data, outfile, indent=4)
  print("Data saving complete.")

def main():
  try:
      year = 2023
      month = 9
      print(f"Fetching tickets for {month}/{year}...")
        tickets = get_tickets_for_month(year, month)

      print(f"Found {len(tickets)} tickets for {month}/{year}.")

      ticket_field_definitions = get_ticket_field_definitions(
      data = transform_tickets(tickets, ticket_field_definitions)
        save_data(data, year, month)

  except Exception as e:
      print(f"An error occurred: {e}")
      if 'data' in locals():
          print(f"Saving the data that was successfully fetched and transformed for {month}/{year}...")
          save_data(data, year, month)

if __name__ == "__main__":
    main()

Publicado 25 sept 2023 · Salim Moumouni

0

Seguidores

1

Voto

0

Comentarios


Salim Moumouni hizo un comentario,

Comentario de la comunidad Feedback - Ticketing system (Support)

Chris Fassano Any chance I could how you setup the triggers? Looking to do the same thing.

Currently, I have it as: 

Meet ALL of the following conditions

  1. Drop down value from the visible field is X
  2. Drop down value from the invisible field is not Y

Action

  1. Drop down value from the invisible field is Y

Not sure if I have to add a "Ticket is updated" clause or if this is fine

Ver comentario · Publicado 27 jun 2022 · Salim Moumouni

0

Seguidores

0

Votos

0

Comentarios


Salim Moumouni hizo un comentario,

Comentario de la comunidad Discussion - Tips and best practices from the community

Tipene Hughes 

Thanks for the tips, I'll see what I can figure out, I only know basic CSS!

Ver comentario · Publicado 21 jun 2022 · Salim Moumouni

0

Seguidores

0

Votos

0

Comentarios


Salim Moumouni hizo un comentario,

Comentario de la comunidad Q&A - Chat, messaging, and widgets

Awesome, thank you Eric Nelson , that should work to stop the contact form from opening after the button is clicked.

Thanks as well for the second suggestion!

Ver comentario · Publicado 15 jun 2022 · Salim Moumouni

0

Seguidores

0

Votos

0

Comentarios


Salim Moumouni hizo un comentario,

Comentario de la comunidad Q&A - Chat, messaging, and widgets

Thanks Eric Nelson, and would this be applied to the original Zendesk widget snippet or the modifications we made to have it open the help center if clicked?

 

Would I simply change 

zE("webWidget:on", "open", function () {

TO

zE("webWidget:on", "close", function () {

 

Ver comentario · Publicado 13 jun 2022 · Salim Moumouni

0

Seguidores

0

Votos

0

Comentarios


Salim Moumouni hizo un comentario,

Comentario de la comunidad Discussion - Tips and best practices from the community

Can this be done with videos on the mobile web widget? So if you click a video on the web widget it opens up as a modal/lightbox

Ver comentario · Publicado 13 jun 2022 · Salim Moumouni

0

Seguidores

0

Votos

0

Comentarios


Salim Moumouni hizo un comentario,

Comentario de la comunidad Q&A - Chat, messaging, and widgets

Hi Eric Nelson , Tipene Hughes,

So we implemented the code and it does redirect to the help center when the web widget is clicked which is great! However, when you go back to the website where the web widget lives, the contact form is automatically opened. Is there a way to keep the widget in its original state and not have the form open when clicked?

 

Ver comentario · Editado 13 jun 2022 · Salim Moumouni

0

Seguidores

0

Votos

0

Comentarios


Salim Moumouni hizo un comentario,

Comentario de la comunidad Discussion - Tips and best practices from the community

Are there any plans to have accordions in the web widget?

Ver comentario · Publicado 28 abr 2022 · Salim Moumouni

0

Seguidores

0

Votos

0

Comentarios


Salim Moumouni hizo un comentario,

Comentario de la comunidad Q&A - Chat, messaging, and widgets

Hi Christopher Kennedy

Would it be possible to have it open in a new tab rather than the same tab?

 

Ver comentario · Publicado 25 abr 2022 · Salim Moumouni

0

Seguidores

0

Votos

0

Comentarios


Salim Moumouni creó una publicación,

Publicación Q&A - Chat, messaging, and widgets

We want to add a CTA for our customers to be able to open up the full help center from the web widget.

So, the behavior would be something along things lines: users search for content on the widget but, if they wish to, they have the option to get redirected to the help center. I know there's that little window at the top right you can click when you're on an actual article but our customers are usually not catching it.

We would like to have this CTA at the bottom of the widget - a simple message (hyperlinked) saying "View our help center".

See below for more context on placement of this CTA - we want to CTA to always appear. on the widget, regardless of the search. 

Publicado 12 abr 2022 · Salim Moumouni

0

Seguidores

4

Votos

3

Comentarios