bugs fixed

This commit is contained in:
Aditya Vaish 2025-10-16 10:34:08 +05:30
parent 6037a0dc83
commit 92c0474df4
4 changed files with 88 additions and 23 deletions

View file

@ -5,7 +5,7 @@ A module for retrieving data from ClickUp.
Allows fetching tasks from workspaces and lists. Allows fetching tasks from workspaces and lists.
""" """
from datetime import datetime from datetime import datetime, timezone
from typing import Any from typing import Any
import requests import requests
@ -180,40 +180,79 @@ class ClickUpConnector:
start_timestamp = int(start_datetime.timestamp() * 1000) start_timestamp = int(start_datetime.timestamp() * 1000)
end_timestamp = int(end_datetime.timestamp() * 1000) end_timestamp = int(end_datetime.timestamp() * 1000)
params = { # Base parameters for both requests
base_params = {
"page": 0, "page": 0,
"order_by": "created", "order_by": "created",
"reverse": "true", "reverse": "true",
"subtasks": "true", "subtasks": "true",
"include_closed": str(include_closed).lower(), "include_closed": str(include_closed).lower(),
# Date filtering - filter by both created and updated dates
"date_created_gt": start_timestamp,
"date_created_lt": end_timestamp,
"date_updated_gt": start_timestamp,
"date_updated_lt": end_timestamp,
} }
all_tasks = [] # Request 1: Filter by creation date
page = 0 created_params = base_params.copy()
created_params.update({
"date_created_gt": start_timestamp,
"date_created_lt": end_timestamp,
})
while True: # Request 2: Filter by update date
params["page"] = page updated_params = base_params.copy()
result = self.make_api_request(f"team/{workspace_id}/task", params) updated_params.update({
"date_updated_gt": start_timestamp,
"date_updated_lt": end_timestamp,
})
if not isinstance(result, dict) or "tasks" not in result: def fetch_tasks_with_params(params):
return [], "Invalid response from ClickUp API" """Helper function to fetch all tasks for given parameters"""
all_tasks = []
page = 0
tasks = result["tasks"] while True:
if not tasks: params["page"] = page
break result = self.make_api_request(f"team/{workspace_id}/task", params)
all_tasks.extend(tasks) if not isinstance(result, dict) or "tasks" not in result:
return [], "Invalid response from ClickUp API"
# Check if there are more pages tasks = result["tasks"]
if len(tasks) < 100: # ClickUp returns max 100 tasks per page if not tasks:
break break
page += 1 all_tasks.extend(tasks)
# Check if there are more pages
if len(tasks) < 100: # ClickUp returns max 100 tasks per page
break
page += 1
return all_tasks, None
# Fetch tasks created in date range
created_tasks, error = fetch_tasks_with_params(created_params)
if error:
return [], error
# Fetch tasks updated in date range
updated_tasks, error = fetch_tasks_with_params(updated_params)
if error:
return [], error
# Merge and deduplicate tasks by ID
# Use a dictionary to store unique tasks (ID as key)
unique_tasks = {}
# Add created tasks
for task in created_tasks:
unique_tasks[task["id"]] = task
# Add updated tasks (will overwrite duplicates, keeping the latest)
for task in updated_tasks:
unique_tasks[task["id"]] = task
# Convert back to list
all_tasks = list(unique_tasks.values())
if not all_tasks: if not all_tasks:
return [], "No tasks found in the specified date range." return [], "No tasks found in the specified date range."

View file

@ -336,6 +336,8 @@ class ConnectorSchedule(BaseModel, TimestampMixin):
) )
schedule_type = Column(SQLAlchemyEnum(ScheduleType), nullable=False) schedule_type = Column(SQLAlchemyEnum(ScheduleType), nullable=False)
cron_expression = Column(String(100), nullable=True) cron_expression = Column(String(100), nullable=True)
# Timezone for interpreting daily_time and weekly_time (IANA zone name)
timezone = Column(String(50), nullable=False, default="UTC")
# Optional schedule config fields (persist user selections) # Optional schedule config fields (persist user selections)
daily_time = Column(Time(timezone=False), nullable=True) daily_time = Column(Time(timezone=False), nullable=True)
weekly_day = Column(Integer, nullable=True) # 0=Mon .. 6=Sun weekly_day = Column(Integer, nullable=True) # 0=Mon .. 6=Sun
@ -445,6 +447,11 @@ if config.AUTH_TYPE == "GOOGLE":
"OAuthAccount", lazy="joined" "OAuthAccount", lazy="joined"
) )
search_spaces = relationship("SearchSpace", back_populates="user") search_spaces = relationship("SearchSpace", back_populates="user")
search_source_connectors = relationship(
"SearchSourceConnector",
back_populates="user",
cascade="all, delete-orphan",
)
search_space_preferences = relationship( search_space_preferences = relationship(
"UserSearchSpacePreference", "UserSearchSpacePreference",
back_populates="user", back_populates="user",

View file

@ -10,10 +10,11 @@ from sqlalchemy import select, Integer
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload from sqlalchemy.orm import selectinload
from app.db import get_async_session, Log from app.db import get_async_session, Log, ConnectorSchedule, SearchSourceConnector
from app.schemas import ConnectorScheduleRead from app.schemas import ConnectorScheduleRead
from app.services.connector_scheduler_service import get_scheduler from app.services.connector_scheduler_service import get_scheduler
from app.users import User, current_active_user from app.users import User, current_active_user
from app.utils.check_ownership import check_ownership
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -49,6 +50,7 @@ async def get_scheduler_status(
async def force_execute_schedule( async def force_execute_schedule(
schedule_id: int, schedule_id: int,
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user), user: User = Depends(current_active_user),
): ):
""" """
@ -58,6 +60,20 @@ async def force_execute_schedule(
the connector sync for the specified schedule. the connector sync for the specified schedule.
""" """
try: try:
# First, verify ownership of the schedule
# Load the schedule and verify the user owns the associated connector
result = await session.execute(
select(ConnectorSchedule).filter(ConnectorSchedule.id == schedule_id)
)
schedule = result.scalars().first()
if not schedule:
raise HTTPException(status_code=404, detail="Schedule not found")
# Verify schedule's connector belongs to user
await check_ownership(session, SearchSourceConnector, schedule.connector_id, user)
# Only after ownership is confirmed, proceed with scheduling
scheduler = await get_scheduler() scheduler = await get_scheduler()
# Add the force execution to background tasks # Add the force execution to background tasks
@ -72,6 +88,8 @@ async def force_execute_schedule(
"schedule_id": schedule_id "schedule_id": schedule_id
} }
except HTTPException:
raise
except Exception as e: except Exception as e:
logger.error(f"Error force executing schedule {schedule_id}: {e}") logger.error(f"Error force executing schedule {schedule_id}: {e}")
raise HTTPException( raise HTTPException(

View file

@ -14,6 +14,7 @@ class ConnectorScheduleBase(BaseModel):
schedule_type: ScheduleType schedule_type: ScheduleType
cron_expression: str | None = None cron_expression: str | None = None
is_active: bool = True is_active: bool = True
timezone: str = "UTC" # IANA timezone name for interpreting daily_time and weekly_time
# Enhanced time selection options # Enhanced time selection options
daily_time: Optional[time] = None # For DAILY schedules (default: 02:00) daily_time: Optional[time] = None # For DAILY schedules (default: 02:00)