Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, Request
|
| 2 |
+
import requests
|
| 3 |
+
import logging
|
| 4 |
+
import os, json
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from google.oauth2 import service_account
|
| 7 |
+
from googleapiclient.discovery import build
|
| 8 |
+
|
| 9 |
+
# Existing service account JSON dictionary
|
| 10 |
+
|
| 11 |
+
# Retrieve the secret as a string
|
| 12 |
+
google_credentials = os.getenv("GOOGLE_CREDENTIALS")
|
| 13 |
+
|
| 14 |
+
# Parse the secret if it's stored as a JSON string
|
| 15 |
+
if google_credentials:
|
| 16 |
+
json_api_dict = json.loads(google_credentials)
|
| 17 |
+
print("Valid Credentials")
|
| 18 |
+
else:
|
| 19 |
+
print("No secret found.")
|
| 20 |
+
|
| 21 |
+
app = FastAPI()
|
| 22 |
+
logging.basicConfig(level=logging.INFO)
|
| 23 |
+
|
| 24 |
+
# Set your credentials
|
| 25 |
+
CUSTOM_FIELD_ID = os.getenv("CUSTOM_FIELD_ID")
|
| 26 |
+
|
| 27 |
+
ACCESS_TOKEN = os.getenv("ACCESS_TOKEN")
|
| 28 |
+
|
| 29 |
+
def update_custom_field(task_id: str, value: str):
|
| 30 |
+
"""Updates the ClickUp task's custom field."""
|
| 31 |
+
url = f"https://api.clickup.com/api/v2/task/{task_id}/field/{CUSTOM_FIELD_ID}"
|
| 32 |
+
headers = {
|
| 33 |
+
"Authorization": ACCESS_TOKEN,
|
| 34 |
+
"Content-Type": "application/json"
|
| 35 |
+
}
|
| 36 |
+
payload = {"value": value} # Payload should be a dictionary
|
| 37 |
+
|
| 38 |
+
# Corrected to pass the payload correctly
|
| 39 |
+
response = requests.post(url, json=payload, headers=headers)
|
| 40 |
+
|
| 41 |
+
if response.status_code == 200:
|
| 42 |
+
print(f"Custom field updated successfully for task {task_id}")
|
| 43 |
+
else:
|
| 44 |
+
print(f"Failed to update custom field: {response.text}")
|
| 45 |
+
|
| 46 |
+
def create_drive_folder(folder_name, month):
|
| 47 |
+
creds = service_account.Credentials.from_service_account_info(json_api_dict)
|
| 48 |
+
drive_service = build('drive', 'v3', credentials=creds)
|
| 49 |
+
parent_folder_id = os.getenv("PARENT_FOLDER_ID")
|
| 50 |
+
|
| 51 |
+
def search_folder(name, parent_id):
|
| 52 |
+
query = f"name='{name}' and '{parent_id}' in parents and mimeType='application/vnd.google-apps.folder' and trashed=false"
|
| 53 |
+
results = drive_service.files().list(q=query, fields='files(id, name, webViewLink)').execute()
|
| 54 |
+
files = results.get('files', [])
|
| 55 |
+
return files[0] if files else None
|
| 56 |
+
|
| 57 |
+
month_folder = search_folder(month, parent_folder_id)
|
| 58 |
+
if not month_folder:
|
| 59 |
+
month_metadata = {'name': month, 'mimeType': 'application/vnd.google-apps.folder', 'parents': [parent_folder_id]}
|
| 60 |
+
month_folder = drive_service.files().create(body=month_metadata, fields='id, webViewLink').execute()
|
| 61 |
+
|
| 62 |
+
target_folder = search_folder(folder_name, month_folder['id'])
|
| 63 |
+
if target_folder:
|
| 64 |
+
return target_folder['webViewLink']
|
| 65 |
+
|
| 66 |
+
folder_metadata = {'name': folder_name, 'mimeType': 'application/vnd.google-apps.folder', 'parents': [month_folder['id']]}
|
| 67 |
+
folder = drive_service.files().create(body=folder_metadata, fields='id, webViewLink').execute()
|
| 68 |
+
return folder['webViewLink']
|
| 69 |
+
|
| 70 |
+
@app.post("/webhook")
|
| 71 |
+
async def receive_webhook(request: Request):
|
| 72 |
+
"""Receives ClickUp webhook events for new tasks."""
|
| 73 |
+
try:
|
| 74 |
+
# Parse the JSON payload
|
| 75 |
+
data = await request.json()
|
| 76 |
+
logging.info(f"Received webhook: {data}")
|
| 77 |
+
|
| 78 |
+
# Check if the event is 'taskCreated'
|
| 79 |
+
if data.get("event") == "taskCreated":
|
| 80 |
+
task_id = data.get("task_id") # Task ID is in the top level of the data
|
| 81 |
+
history_items = data.get("history_items", [])
|
| 82 |
+
|
| 83 |
+
task_name = "Unnamed Task" # Default if no name is found
|
| 84 |
+
creation_timestamp = None # Placeholder for creation timestamp
|
| 85 |
+
|
| 86 |
+
# Loop through history_items to find task creation and task name
|
| 87 |
+
for item in history_items:
|
| 88 |
+
if item.get("field") == "task_creation":
|
| 89 |
+
# Ensure 'date' is an integer before division
|
| 90 |
+
creation_timestamp = int(item.get("date", 0)) # Convert to int if necessary
|
| 91 |
+
|
| 92 |
+
# If we have a task_id, make an API call to get task details
|
| 93 |
+
if task_id:
|
| 94 |
+
task_name = get_task_details(task_id)
|
| 95 |
+
|
| 96 |
+
# If a creation timestamp is found, convert it to a month
|
| 97 |
+
if creation_timestamp:
|
| 98 |
+
creation_date = datetime.utcfromtimestamp(creation_timestamp / 1000) # Convert from milliseconds
|
| 99 |
+
creation_month = creation_date.strftime("%b %Y") # Format it as "Mon Year" (e.g., "Feb 2025")
|
| 100 |
+
else:
|
| 101 |
+
creation_month = "Unknown Month"
|
| 102 |
+
|
| 103 |
+
logging.info(f"New Task Created: {task_name} (ID: {task_id})")
|
| 104 |
+
logging.info(f"Task Creation Month: {creation_month}")
|
| 105 |
+
|
| 106 |
+
else:
|
| 107 |
+
logging.info(f"Received a different event: {data.get('event')}")
|
| 108 |
+
|
| 109 |
+
except Exception as e:
|
| 110 |
+
logging.error(f"Error processing webhook: {e}")
|
| 111 |
+
|
| 112 |
+
# Update custom field "1- Drive Link" with a sample value
|
| 113 |
+
drive_link = create_drive_folder(task_name, creation_month)
|
| 114 |
+
update_custom_field(task_id, drive_link)
|
| 115 |
+
|
| 116 |
+
return {"status": "success"}
|
| 117 |
+
|
| 118 |
+
def get_task_details(task_id: str):
|
| 119 |
+
"""Fetch task details from ClickUp API using task_id."""
|
| 120 |
+
url = f"https://api.clickup.com/api/v2/task/{task_id}"
|
| 121 |
+
headers = {
|
| 122 |
+
"Authorization": ACCESS_TOKEN
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
# Make the API request to ClickUp
|
| 126 |
+
response = requests.get(url, headers=headers)
|
| 127 |
+
|
| 128 |
+
# Check if the request was successful (status code 200)
|
| 129 |
+
if response.status_code == 200:
|
| 130 |
+
# Parse and return the task details
|
| 131 |
+
|
| 132 |
+
task_name = response.json()["name"]
|
| 133 |
+
return task_name
|
| 134 |
+
else:
|
| 135 |
+
task_name = "Task name wasn't not found"
|
| 136 |
+
return task_name
|