ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: Voice Tasker preview и трейс активности

This commit is contained in:
DCCONSTRUCTIONS
2026-04-26 20:41:38 +03:00
parent d867a89a1b
commit 9a91af372e
10 changed files with 588 additions and 289 deletions
@@ -69,6 +69,7 @@ from .workspace.label import WorkspaceLabelsEndpoint
from .workspace.state import WorkspaceStatesEndpoint
from .workspace.user import (
UserLastProjectWithWorkspaceEndpoint,
WorkspaceActivityEndpoint,
WorkspaceUserProfileIssuesEndpoint,
WorkspaceUserPropertiesEndpoint,
WorkspaceUserProfileEndpoint,
@@ -2589,66 +2589,145 @@ class VoiceTaskCommitEndpoint(BaseAPIView):
)
if action == "create_task":
project = Project.objects.get(id=resolution["project"]["id"], workspace=workspace)
payload = build_voice_task_issue_payload(draft, resolution, voice_session.transcript)
payload_without_project = {key: value for key, value in payload.items() if key != "project_id"}
payload_without_project["external_source"] = VOICE_TASK_EXTERNAL_SOURCE
payload_without_project["external_id"] = str(voice_session.id)
existing_issue = voice_session.created_task
if is_voice_task_issue_available(existing_issue):
issue = existing_issue
if not can_user_update_voice_task_issue(request.user, workspace, issue):
return Response(
{
"ok": False,
"code": "issue_permission_denied",
"error": "Voice Task draft could not update the created work item.",
},
status=status.HTTP_403_FORBIDDEN,
)
serializer = IssueCreateSerializer(
data=payload_without_project,
context={
"project_id": project.id,
"workspace_id": workspace.id,
"default_assignee_id": project.default_assignee_id,
},
)
if not serializer.is_valid():
return Response(
{
"ok": False,
"code": "issue_validation_failed",
"error": "Voice Task draft could not be converted to a work item.",
"details": serializer.errors,
},
status=status.HTTP_400_BAD_REQUEST,
project = Project.objects.get(id=resolution["project"]["id"], workspace=workspace)
payload = build_voice_task_issue_payload(draft, resolution, voice_session.transcript)
payload_without_project = {key: value for key, value in payload.items() if key != "project_id"}
payload_without_project["external_source"] = VOICE_TASK_EXTERNAL_SOURCE
payload_without_project["external_id"] = str(voice_session.id)
current_instance = json.dumps(IssueDetailSerializer(issue).data, cls=DjangoJSONEncoder)
requested_data = json.dumps(
{**payload_without_project, "project_id": str(project.id)},
cls=DjangoJSONEncoder,
)
issue = serializer.save(created_by_id=request.user.id)
voice_session.created_task = issue
voice_session.parsed_json = draft
voice_session.save(update_fields=["created_task", "parsed_json", "updated_at"])
if issue.project_id != project.id:
issue = move_voice_task_issue_to_project(issue, project, resolution["state"], request.user)
requested_data = json.dumps(payload_without_project, cls=DjangoJSONEncoder)
issue_activity.delay(
type="issue.activity.created",
requested_data=requested_data,
actor_id=str(request.user.id),
issue_id=str(issue.id),
project_id=str(project.id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=base_host(request=request, is_app=True),
)
model_activity.delay(
model_name="issue",
model_id=str(issue.id),
requested_data=payload_without_project,
current_instance=None,
actor_id=request.user.id,
slug=slug,
origin=base_host(request=request, is_app=True),
)
issue_description_version_task.delay(
updated_issue=requested_data,
issue_id=str(issue.id),
user_id=request.user.id,
is_creating=True,
)
serializer = IssueCreateSerializer(
issue,
data=payload_without_project,
partial=True,
context={"project_id": project.id},
)
if not serializer.is_valid():
return Response(
{
"ok": False,
"code": "issue_validation_failed",
"error": "Voice Task draft could not update the created work item.",
"details": serializer.errors,
},
status=status.HTTP_400_BAD_REQUEST,
)
response_status = status.HTTP_201_CREATED
commit_status = "created"
serializer.save()
issue.refresh_from_db()
voice_session.created_task = issue
voice_session.parsed_json = draft
voice_session.save(update_fields=["created_task", "parsed_json", "updated_at"])
issue_activity.delay(
type="issue.activity.updated",
requested_data=requested_data,
actor_id=str(request.user.id),
issue_id=str(issue.id),
project_id=str(project.id),
current_instance=current_instance,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=base_host(request=request, is_app=True),
)
model_activity.delay(
model_name="issue",
model_id=str(issue.id),
requested_data=json.loads(requested_data),
current_instance=current_instance,
actor_id=request.user.id,
slug=slug,
origin=base_host(request=request, is_app=True),
)
issue_description_version_task.delay(
updated_issue=current_instance,
issue_id=str(issue.id),
user_id=request.user.id,
)
response_status = status.HTTP_200_OK
commit_status = "updated"
else:
project = Project.objects.get(id=resolution["project"]["id"], workspace=workspace)
payload = build_voice_task_issue_payload(draft, resolution, voice_session.transcript)
payload_without_project = {key: value for key, value in payload.items() if key != "project_id"}
payload_without_project["external_source"] = VOICE_TASK_EXTERNAL_SOURCE
payload_without_project["external_id"] = str(voice_session.id)
serializer = IssueCreateSerializer(
data=payload_without_project,
context={
"project_id": project.id,
"workspace_id": workspace.id,
"default_assignee_id": project.default_assignee_id,
},
)
if not serializer.is_valid():
return Response(
{
"ok": False,
"code": "issue_validation_failed",
"error": "Voice Task draft could not be converted to a work item.",
"details": serializer.errors,
},
status=status.HTTP_400_BAD_REQUEST,
)
issue = serializer.save(created_by_id=request.user.id)
voice_session.created_task = issue
voice_session.parsed_json = draft
voice_session.save(update_fields=["created_task", "parsed_json", "updated_at"])
requested_data = json.dumps(payload_without_project, cls=DjangoJSONEncoder)
issue_activity.delay(
type="issue.activity.created",
requested_data=requested_data,
actor_id=str(request.user.id),
issue_id=str(issue.id),
project_id=str(project.id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=base_host(request=request, is_app=True),
)
model_activity.delay(
model_name="issue",
model_id=str(issue.id),
requested_data=payload_without_project,
current_instance=None,
actor_id=request.user.id,
slug=slug,
origin=base_host(request=request, is_app=True),
)
issue_description_version_task.delay(
updated_issue=requested_data,
issue_id=str(issue.id),
user_id=request.user.id,
is_creating=True,
)
response_status = status.HTTP_201_CREATED
commit_status = "created"
elif action == "update_task":
target_task = resolution.get("target_task") or {}
@@ -406,6 +406,35 @@ class WorkspaceUserActivityEndpoint(BaseAPIView):
)
class WorkspaceActivityEndpoint(BaseAPIView):
permission_classes = [WorkspaceEntityPermission]
def get(self, request, slug):
projects = request.query_params.getlist("project", [])
queryset = (
IssueActivity.objects.filter(
~Q(field__in=["comment", "vote", "reaction", "draft"]),
workspace__slug=slug,
project__project_projectmember__member=request.user,
project__project_projectmember__is_active=True,
project__archived_at__isnull=True,
)
.select_related("actor", "workspace", "issue", "project")
.distinct()
)
if projects:
queryset = queryset.filter(project__in=projects)
return self.paginate(
order_by=request.GET.get("order_by", "-created_at"),
request=request,
queryset=queryset,
on_results=lambda issue_activities: IssueActivitySerializer(issue_activities, many=True).data,
)
class WorkspaceUserProfileStatsEndpoint(BaseAPIView):
def get(self, request, slug, user_id):
filters = issue_filters(request.query_params, "GET")