mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-22 23:31:12 +02:00
bugs fixed
This commit is contained in:
parent
6037a0dc83
commit
92c0474df4
4 changed files with 88 additions and 23 deletions
|
|
@ -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."
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue