refactor: move issue activity tracking from django-channels to rqworker (#207)

* refactor: move issue activity tracking from django channels to rqworker

* refactor: update old value and new value from character type to text type
This commit is contained in:
pablohashescobar 2023-01-31 00:46:59 +05:30 committed by GitHub
parent 0ff5f363ee
commit 45a52116a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 566 additions and 761 deletions

View File

@ -49,7 +49,7 @@ USER root
RUN apk --update --no-cache add "bash~=5.1" RUN apk --update --no-cache add "bash~=5.1"
COPY ./bin ./bin/ COPY ./bin ./bin/
RUN chmod +x ./bin/channel-worker ./bin/takeoff ./bin/worker RUN chmod +x ./bin/takeoff ./bin/worker
USER captain USER captain

View File

@ -1,3 +1,2 @@
web: gunicorn -w 4 -k uvicorn.workers.UvicornWorker plane.asgi:application --bind 0.0.0.0:$PORT --config gunicorn.config.py --max-requests 10000 --max-requests-jitter 1000 --access-logfile - web: gunicorn -w 4 -k uvicorn.workers.UvicornWorker plane.asgi:application --bind 0.0.0.0:$PORT --config gunicorn.config.py --max-requests 10000 --max-requests-jitter 1000 --access-logfile -
worker: python manage.py rqworker worker: python manage.py rqworker
channel-worker: python manage.py runworker issue-activites

View File

@ -1,6 +0,0 @@
#!/bin/bash
set -e
python manage.py wait_for_db
python manage.py migrate
python manage.py runworker issue-activites

View File

@ -1 +0,0 @@
from .issue_consumer import IssueConsumer

View File

@ -1,547 +0,0 @@
from channels.generic.websocket import SyncConsumer
import json
from plane.db.models import IssueActivity, Project, User, Issue, State, Label
class IssueConsumer(SyncConsumer):
# Track Chnages in name
def track_name(
self,
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("name") != requested_data.get("name"):
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("name"),
new_value=requested_data.get("name"),
field="name",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the start date to {requested_data.get('name')}",
)
)
# Track changes in parent issue
def track_parent(
self,
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("parent") != requested_data.get("parent"):
if requested_data.get("parent") == None:
old_parent = Issue.objects.get(pk=current_instance.get("parent"))
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=f"{project.identifier}-{old_parent.sequence_id}",
new_value=None,
field="parent",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the parent issue to None",
old_identifier=old_parent.id,
new_identifier=None,
)
)
else:
new_parent = Issue.objects.get(pk=requested_data.get("parent"))
old_parent = Issue.objects.filter(
pk=current_instance.get("parent")
).first()
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=f"{project.identifier}-{old_parent.sequence_id}"
if old_parent is not None
else None,
new_value=f"{project.identifier}-{new_parent.sequence_id}",
field="parent",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the parent issue to {new_parent.name}",
old_identifier=old_parent.id
if old_parent is not None
else None,
new_identifier=new_parent.id,
)
)
# Track changes in priority
def track_priority(
self,
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("priority") != requested_data.get("priority"):
if requested_data.get("priority") == None:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("parent"),
new_value=requested_data.get("parent"),
field="priority",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the priority to None",
)
)
else:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("priority"),
new_value=requested_data.get("priority"),
field="priority",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the priority to {requested_data.get('priority')}",
)
)
# Track chnages in state of the issue
def track_state(
self,
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("state") != requested_data.get("state"):
new_state = State.objects.get(pk=requested_data.get("state", None))
old_state = State.objects.get(pk=current_instance.get("state", None))
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=old_state.name,
new_value=new_state.name,
field="state",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the state to {new_state.name}",
old_identifier=old_state.id,
new_identifier=new_state.id,
)
)
# Track issue description
def track_description(
self,
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("description_html") != requested_data.get("description_html"):
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("description_html"),
new_value=requested_data.get("description_html"),
field="description",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the description to {requested_data.get('description_html')}",
)
)
# Track changes in issue target date
def track_target_date(
self,
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("target_date") != requested_data.get("target_date"):
if requested_data.get("target_date") == None:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("target_date"),
new_value=requested_data.get("target_date"),
field="target_date",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the target date to None",
)
)
else:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("target_date"),
new_value=requested_data.get("target_date"),
field="target_date",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the target date to {requested_data.get('target_date')}",
)
)
# Track changes in issue start date
def track_start_date(
self,
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("start_date") != requested_data.get("start_date"):
if requested_data.get("start_date") == None:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("start_date"),
new_value=requested_data.get("start_date"),
field="start_date",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the start date to None",
)
)
else:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("start_date"),
new_value=requested_data.get("start_date"),
field="start_date",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the start date to {requested_data.get('start_date')}",
)
)
# Track changes in issue labels
def track_labels(
self,
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
# Label Addition
if len(requested_data.get("labels_list")) > len(current_instance.get("labels")):
for label in requested_data.get("labels_list"):
if label not in current_instance.get("labels"):
label = Label.objects.get(pk=label)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value="",
new_value=label.name,
field="labels",
project=project,
workspace=project.workspace,
comment=f"{actor.email} added label {label.name}",
new_identifier=label.id,
old_identifier=None,
)
)
# Label Removal
if len(requested_data.get("labels_list")) < len(current_instance.get("labels")):
for label in current_instance.get("labels"):
if label not in requested_data.get("labels_list"):
label = Label.objects.get(pk=label)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=label.name,
new_value="",
field="labels",
project=project,
workspace=project.workspace,
comment=f"{actor.email} removed label {label.name}",
old_identifier=label.id,
new_identifier=None,
)
)
# Track changes in issue assignees
def track_assignees(
self,
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
# Assignee Addition
if len(requested_data.get("assignees_list")) > len(
current_instance.get("assignees")
):
for assignee in requested_data.get("assignees_list"):
if assignee not in current_instance.get("assignees"):
assignee = User.objects.get(pk=assignee)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value="",
new_value=assignee.email,
field="assignees",
project=project,
workspace=project.workspace,
comment=f"{actor.email} added assignee {assignee.email}",
new_identifier=actor.id,
)
)
# Assignee Removal
if len(requested_data.get("assignees_list")) < len(
current_instance.get("assignees")
):
for assignee in current_instance.get("assignees"):
if assignee not in requested_data.get("assignees_list"):
assignee = User.objects.get(pk=assignee)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=assignee.email,
new_value="",
field="assignee",
project=project,
workspace=project.workspace,
comment=f"{actor.email} removed assignee {assignee.email}",
old_identifier=actor.id,
)
)
# Track changes in blocking issues
def track_blocks(
self,
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if len(requested_data.get("blocks_list")) > len(
current_instance.get("blocked_issues")
):
for block in requested_data.get("blocks_list"):
if (
len(
[
blocked
for blocked in current_instance.get("blocked_issues")
if blocked.get("block") == block
]
)
== 0
):
issue = Issue.objects.get(pk=block)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value="",
new_value=f"{project.identifier}-{issue.sequence_id}",
field="blocks",
project=project,
workspace=project.workspace,
comment=f"{actor.email} added blocking issue {project.identifier}-{issue.sequence_id}",
new_identifier=issue.id,
)
)
# Blocked Issue Removal
if len(requested_data.get("blocks_list")) < len(
current_instance.get("blocked_issues")
):
for blocked in current_instance.get("blocked_issues"):
if blocked.get("block") not in requested_data.get("blocks_list"):
issue = Issue.objects.get(pk=blocked.get("block"))
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=f"{project.identifier}-{issue.sequence_id}",
new_value="",
field="blocks",
project=project,
workspace=project.workspace,
comment=f"{actor.email} removed blocking issue {project.identifier}-{issue.sequence_id}",
old_identifier=issue.id,
)
)
# Track changes in blocked_by issues
def track_blockings(
self,
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if len(requested_data.get("blockers_list")) > len(
current_instance.get("blocker_issues")
):
for block in requested_data.get("blockers_list"):
if (
len(
[
blocked
for blocked in current_instance.get("blocker_issues")
if blocked.get("blocked_by") == block
]
)
== 0
):
issue = Issue.objects.get(pk=block)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value="",
new_value=f"{project.identifier}-{issue.sequence_id}",
field="blocking",
project=project,
workspace=project.workspace,
comment=f"{actor.email} added blocked by issue {project.identifier}-{issue.sequence_id}",
new_identifier=issue.id,
)
)
# Blocked Issue Removal
if len(requested_data.get("blockers_list")) < len(
current_instance.get("blocker_issues")
):
for blocked in current_instance.get("blocker_issues"):
if blocked.get("blocked_by") not in requested_data.get("blockers_list"):
issue = Issue.objects.get(pk=blocked.get("blocked_by"))
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=f"{project.identifier}-{issue.sequence_id}",
new_value="",
field="blocking",
project=project,
workspace=project.workspace,
comment=f"{actor.email} removed blocked by issue {project.identifier}-{issue.sequence_id}",
old_identifier=issue.id,
)
)
# Receive message from room group
def issue_activity(self, event):
issue_activities = []
# Remove event type:
event.pop("type")
requested_data = json.loads(event.get("requested_data"))
current_instance = json.loads(event.get("current_instance"))
issue_id = event.get("issue_id")
actor_id = event.get("actor_id")
project_id = event.get("project_id")
actor = User.objects.get(pk=actor_id)
project = Project.objects.get(pk=project_id)
ISSUE_ACTIVITY_MAPPER = {
"name": self.track_name,
"parent": self.track_parent,
"priority": self.track_priority,
"state": self.track_state,
"description": self.track_description,
"target_date": self.track_target_date,
"start_date": self.track_start_date,
"labels_list": self.track_labels,
"assignees_list": self.track_assignees,
"blocks_list": self.track_blocks,
"blockers_list": self.track_blockings,
}
for key in requested_data:
func = ISSUE_ACTIVITY_MAPPER.get(key, None)
if func is not None:
func(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
)
# Save all the values to database
IssueActivity.objects.bulk_create(issue_activities)

View File

@ -10,8 +10,6 @@ from django.core.serializers.json import DjangoJSONEncoder
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from sentry_sdk import capture_exception from sentry_sdk import capture_exception
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
# Module imports # Module imports
from . import BaseViewSet, BaseAPIView from . import BaseViewSet, BaseAPIView
@ -42,6 +40,7 @@ from plane.db.models import (
CycleIssue, CycleIssue,
ModuleIssue, ModuleIssue,
) )
from plane.bgtasks.issue_activites_task import issue_activity
class IssueViewSet(BaseViewSet): class IssueViewSet(BaseViewSet):
@ -72,12 +71,12 @@ class IssueViewSet(BaseViewSet):
def perform_update(self, serializer): def perform_update(self, serializer):
requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder) requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder)
current_instance = Issue.objects.filter(pk=self.kwargs.get("pk", None)).first() current_instance = (
self.get_queryset().filter(pk=self.kwargs.get("pk", None)).first()
)
if current_instance is not None: if current_instance is not None:
channel_layer = get_channel_layer() issue_activity.delay(
async_to_sync(channel_layer.send)(
"issue-activites",
{ {
"type": "issue.activity", "type": "issue.activity",
"requested_data": requested_data, "requested_data": requested_data,

View File

@ -1,6 +1,6 @@
import os import os
from channels.routing import ProtocolTypeRouter, ChannelNameRouter from channels.routing import ProtocolTypeRouter
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
django_asgi_app = get_asgi_application() django_asgi_app = get_asgi_application()
@ -10,15 +10,9 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "plane.settings.production")
# Initialize Django ASGI application early to ensure the AppRegistry # Initialize Django ASGI application early to ensure the AppRegistry
# is populated before importing code that may import ORM models. # is populated before importing code that may import ORM models.
from plane.api.consumers import IssueConsumer
application = ProtocolTypeRouter( application = ProtocolTypeRouter(
{ {
"http": get_asgi_application(), "http": get_asgi_application(),
"channel": ChannelNameRouter(
{
"issue-activites": IssueConsumer.as_asgi(),
}
),
} }
) )

View File

@ -0,0 +1,553 @@
# Python imports
import json
# Third Party imports
from django_rq import job
from sentry_sdk import capture_exception
# Module imports
from plane.db.models import User, Issue, Project, Label, IssueActivity, State
# Track Chnages in name
def track_name(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("name") != requested_data.get("name"):
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("name"),
new_value=requested_data.get("name"),
field="name",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the start date to {requested_data.get('name')}",
)
)
# Track changes in parent issue
def track_parent(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("parent") != requested_data.get("parent"):
if requested_data.get("parent") == None:
old_parent = Issue.objects.get(pk=current_instance.get("parent"))
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=f"{project.identifier}-{old_parent.sequence_id}",
new_value=None,
field="parent",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the parent issue to None",
old_identifier=old_parent.id,
new_identifier=None,
)
)
else:
new_parent = Issue.objects.get(pk=requested_data.get("parent"))
old_parent = Issue.objects.filter(pk=current_instance.get("parent")).first()
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=f"{project.identifier}-{old_parent.sequence_id}"
if old_parent is not None
else None,
new_value=f"{project.identifier}-{new_parent.sequence_id}",
field="parent",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the parent issue to {new_parent.name}",
old_identifier=old_parent.id if old_parent is not None else None,
new_identifier=new_parent.id,
)
)
# Track changes in priority
def track_priority(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("priority") != requested_data.get("priority"):
if requested_data.get("priority") == None:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("priority"),
new_value=None,
field="priority",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the priority to None",
)
)
else:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("priority"),
new_value=requested_data.get("priority"),
field="priority",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the priority to {requested_data.get('priority')}",
)
)
# Track chnages in state of the issue
def track_state(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("state") != requested_data.get("state"):
new_state = State.objects.get(pk=requested_data.get("state", None))
old_state = State.objects.get(pk=current_instance.get("state", None))
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=old_state.name,
new_value=new_state.name,
field="state",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the state to {new_state.name}",
old_identifier=old_state.id,
new_identifier=new_state.id,
)
)
# Track issue description
def track_description(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("description_html") != requested_data.get(
"description_html"
):
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("description_html"),
new_value=requested_data.get("description_html"),
field="description",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the description to {requested_data.get('description_html')}",
)
)
# Track changes in issue target date
def track_target_date(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("target_date") != requested_data.get("target_date"):
if requested_data.get("target_date") == None:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("target_date"),
new_value=requested_data.get("target_date"),
field="target_date",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the target date to None",
)
)
else:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("target_date"),
new_value=requested_data.get("target_date"),
field="target_date",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the target date to {requested_data.get('target_date')}",
)
)
# Track changes in issue start date
def track_start_date(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if current_instance.get("start_date") != requested_data.get("start_date"):
if requested_data.get("start_date") == None:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("start_date"),
new_value=requested_data.get("start_date"),
field="start_date",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the start date to None",
)
)
else:
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=current_instance.get("start_date"),
new_value=requested_data.get("start_date"),
field="start_date",
project=project,
workspace=project.workspace,
comment=f"{actor.email} updated the start date to {requested_data.get('start_date')}",
)
)
# Track changes in issue labels
def track_labels(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
# Label Addition
if len(requested_data.get("labels_list")) > len(current_instance.get("labels")):
for label in requested_data.get("labels_list"):
if label not in current_instance.get("labels"):
label = Label.objects.get(pk=label)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value="",
new_value=label.name,
field="labels",
project=project,
workspace=project.workspace,
comment=f"{actor.email} added label {label.name}",
new_identifier=label.id,
old_identifier=None,
)
)
# Label Removal
if len(requested_data.get("labels_list")) < len(current_instance.get("labels")):
for label in current_instance.get("labels"):
if label not in requested_data.get("labels_list"):
label = Label.objects.get(pk=label)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=label.name,
new_value="",
field="labels",
project=project,
workspace=project.workspace,
comment=f"{actor.email} removed label {label.name}",
old_identifier=label.id,
new_identifier=None,
)
)
# Track changes in issue assignees
def track_assignees(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
# Assignee Addition
if len(requested_data.get("assignees_list")) > len(
current_instance.get("assignees")
):
for assignee in requested_data.get("assignees_list"):
if assignee not in current_instance.get("assignees"):
assignee = User.objects.get(pk=assignee)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value="",
new_value=assignee.email,
field="assignees",
project=project,
workspace=project.workspace,
comment=f"{actor.email} added assignee {assignee.email}",
new_identifier=actor.id,
)
)
# Assignee Removal
if len(requested_data.get("assignees_list")) < len(
current_instance.get("assignees")
):
for assignee in current_instance.get("assignees"):
if assignee not in requested_data.get("assignees_list"):
assignee = User.objects.get(pk=assignee)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=assignee.email,
new_value="",
field="assignee",
project=project,
workspace=project.workspace,
comment=f"{actor.email} removed assignee {assignee.email}",
old_identifier=actor.id,
)
)
# Track changes in blocking issues
def track_blocks(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if len(requested_data.get("blocks_list")) > len(
current_instance.get("blocked_issues")
):
for block in requested_data.get("blocks_list"):
if (
len(
[
blocked
for blocked in current_instance.get("blocked_issues")
if blocked.get("block") == block
]
)
== 0
):
issue = Issue.objects.get(pk=block)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value="",
new_value=f"{project.identifier}-{issue.sequence_id}",
field="blocks",
project=project,
workspace=project.workspace,
comment=f"{actor.email} added blocking issue {project.identifier}-{issue.sequence_id}",
new_identifier=issue.id,
)
)
# Blocked Issue Removal
if len(requested_data.get("blocks_list")) < len(
current_instance.get("blocked_issues")
):
for blocked in current_instance.get("blocked_issues"):
if blocked.get("block") not in requested_data.get("blocks_list"):
issue = Issue.objects.get(pk=blocked.get("block"))
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=f"{project.identifier}-{issue.sequence_id}",
new_value="",
field="blocks",
project=project,
workspace=project.workspace,
comment=f"{actor.email} removed blocking issue {project.identifier}-{issue.sequence_id}",
old_identifier=issue.id,
)
)
# Track changes in blocked_by issues
def track_blockings(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
):
if len(requested_data.get("blockers_list")) > len(
current_instance.get("blocker_issues")
):
for block in requested_data.get("blockers_list"):
if (
len(
[
blocked
for blocked in current_instance.get("blocker_issues")
if blocked.get("blocked_by") == block
]
)
== 0
):
issue = Issue.objects.get(pk=block)
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value="",
new_value=f"{project.identifier}-{issue.sequence_id}",
field="blocking",
project=project,
workspace=project.workspace,
comment=f"{actor.email} added blocked by issue {project.identifier}-{issue.sequence_id}",
new_identifier=issue.id,
)
)
# Blocked Issue Removal
if len(requested_data.get("blockers_list")) < len(
current_instance.get("blocker_issues")
):
for blocked in current_instance.get("blocker_issues"):
if blocked.get("blocked_by") not in requested_data.get("blockers_list"):
issue = Issue.objects.get(pk=blocked.get("blocked_by"))
issue_activities.append(
IssueActivity(
issue_id=issue_id,
actor=actor,
verb="updated",
old_value=f"{project.identifier}-{issue.sequence_id}",
new_value="",
field="blocking",
project=project,
workspace=project.workspace,
comment=f"{actor.email} removed blocked by issue {project.identifier}-{issue.sequence_id}",
old_identifier=issue.id,
)
)
# Receive message from room group
@job("default")
def issue_activity(event):
try:
issue_activities = []
requested_data = json.loads(event.get("requested_data"))
current_instance = json.loads(event.get("current_instance"))
issue_id = event.get("issue_id")
actor_id = event.get("actor_id")
project_id = event.get("project_id")
actor = User.objects.get(pk=actor_id)
project = Project.objects.get(pk=project_id)
ISSUE_ACTIVITY_MAPPER = {
"name": track_name,
"parent": track_parent,
"priority": track_priority,
"state": track_state,
"description": track_description,
"target_date": track_target_date,
"start_date": track_start_date,
"labels_list": track_labels,
"assignees_list": track_assignees,
"blocks_list": track_blocks,
"blockers_list": track_blockings,
}
for key in requested_data:
func = ISSUE_ACTIVITY_MAPPER.get(key, None)
if func is not None:
func(
requested_data,
current_instance,
issue_id,
project,
actor,
issue_activities,
)
# Save all the values to database
_ = IssueActivity.objects.bulk_create(issue_activities)
return
except Exception as e:
capture_exception(e)
return

View File

@ -1,52 +1,5 @@
from django.apps import AppConfig from django.apps import AppConfig
from fieldsignals import post_save_changed
class DbConfig(AppConfig): class DbConfig(AppConfig):
name = "plane.db" name = "plane.db"
# def ready(self):
# post_save_changed.connect(
# self.model_activity,
# sender=self.get_model("Issue"),
# )
# def model_activity(self, sender, instance, changed_fields, **kwargs):
# verb = "created" if instance._state.adding else "changed"
# import inspect
# for frame_record in inspect.stack():
# if frame_record[3] == "get_response":
# request = frame_record[0].f_locals["request"]
# REQUEST_METHOD = request.method
# if REQUEST_METHOD == "POST":
# self.get_model("IssueActivity").objects.create(
# issue=instance, project=instance.project, actor=instance.created_by
# )
# elif REQUEST_METHOD == "PATCH":
# try:
# del changed_fields["updated_at"]
# del changed_fields["updated_by"]
# except KeyError as e:
# pass
# for field_name, (old, new) in changed_fields.items():
# field = field_name
# old_value = old
# new_value = new
# self.get_model("IssueActivity").objects.create(
# issue=instance,
# verb=verb,
# field=field,
# old_value=old_value,
# new_value=new_value,
# project=instance.project,
# actor=instance.updated_by,
# )

View File

@ -145,12 +145,8 @@ class IssueActivity(ProjectBaseModel):
field = models.CharField( field = models.CharField(
max_length=255, verbose_name="Field Name", blank=True, null=True max_length=255, verbose_name="Field Name", blank=True, null=True
) )
old_value = models.CharField( old_value = models.TextField(verbose_name="Old Value", blank=True, null=True)
max_length=255, verbose_name="Old Value", blank=True, null=True new_value = models.TextField(verbose_name="New Value", blank=True, null=True)
)
new_value = models.CharField(
max_length=255, verbose_name="New Value", blank=True, null=True
)
comment = models.TextField(verbose_name="Comment", blank=True) comment = models.TextField(verbose_name="Comment", blank=True)
attachments = ArrayField(models.URLField(), size=10, blank=True, default=list) attachments = ArrayField(models.URLField(), size=10, blank=True, default=list)

View File

@ -34,9 +34,7 @@ INSTALLED_APPS = [
"rest_framework_simplejwt.token_blacklist", "rest_framework_simplejwt.token_blacklist",
"corsheaders", "corsheaders",
"taggit", "taggit",
"fieldsignals",
"django_rq", "django_rq",
"channels",
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View File

@ -66,11 +66,3 @@ RQ_QUEUES = {
WEB_URL = "http://localhost:3000" WEB_URL = "http://localhost:3000"
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [(REDIS_HOST, REDIS_PORT)],
},
},
}

View File

@ -1,11 +1,8 @@
"""Production settings and globals.""" """Production settings and globals."""
import ssl
from typing import Optional
from urllib.parse import urlparse from urllib.parse import urlparse
import dj_database_url import dj_database_url
from urllib.parse import urlparse from urllib.parse import urlparse
from redis.asyncio.connection import Connection, RedisSSLContext
import sentry_sdk import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.django import DjangoIntegration
@ -186,64 +183,10 @@ RQ_QUEUES = {
} }
class CustomSSLConnection(Connection):
def __init__(
self,
ssl_context: Optional[str] = None,
**kwargs,
):
super().__init__(**kwargs)
self.ssl_context = RedisSSLContext(ssl_context)
class RedisSSLContext:
__slots__ = ("context",)
def __init__(
self,
ssl_context,
):
self.context = ssl_context
def get(self):
return self.context
url = urlparse(os.environ.get("REDIS_URL")) url = urlparse(os.environ.get("REDIS_URL"))
DOCKERIZED = os.environ.get("DOCKERIZED", False) # Set the variable true if running in docker-compose environment DOCKERIZED = os.environ.get(
"DOCKERIZED", False
if not DOCKERIZED: ) # Set the variable true if running in docker-compose environment
ssl_context = ssl.SSLContext()
ssl_context.check_hostname = False
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [
{
"host": url.hostname,
"port": url.port,
"username": url.username,
"password": url.password,
"connection_class": CustomSSLConnection,
"ssl_context": ssl_context,
}
],
},
},
}
else:
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [(os.environ.get("REDIS_URL"))],
},
},
}
WEB_URL = os.environ.get("WEB_URL") WEB_URL = os.environ.get("WEB_URL")

View File

@ -1,11 +1,8 @@
"""Production settings and globals.""" """Production settings and globals."""
import ssl
from typing import Optional
from urllib.parse import urlparse from urllib.parse import urlparse
import dj_database_url import dj_database_url
from urllib.parse import urlparse from urllib.parse import urlparse
from redis.asyncio.connection import Connection, RedisSSLContext
import sentry_sdk import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.django import DjangoIntegration
@ -186,52 +183,5 @@ RQ_QUEUES = {
} }
} }
class CustomSSLConnection(Connection):
def __init__(
self,
ssl_context: Optional[str] = None,
**kwargs,
):
super().__init__(**kwargs)
self.ssl_context = RedisSSLContext(ssl_context)
class RedisSSLContext:
__slots__ = (
"context",
)
def __init__(
self,
ssl_context,
):
self.context = ssl_context
def get(self):
return self.context
url = urlparse(os.environ.get("REDIS_URL"))
ssl_context = ssl.SSLContext()
ssl_context.check_hostname = False
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [
{
'host': url.hostname,
'port': url.port,
'username': url.username,
'password': url.password,
'connection_class': CustomSSLConnection,
'ssl_context': ssl_context,
}
],
}
},
}
WEB_URL = os.environ.get("WEB_URL") WEB_URL = os.environ.get("WEB_URL")

View File

@ -20,12 +20,9 @@ sentry-sdk==1.13.0
django-s3-storage==0.13.6 django-s3-storage==0.13.6
django-crum==0.7.9 django-crum==0.7.9
django-guardian==2.4.0 django-guardian==2.4.0
django-fieldsignals==0.7.0
dj_rest_auth==2.2.5 dj_rest_auth==2.2.5
google-auth==2.9.1 google-auth==2.9.1
google-api-python-client==2.55.0 google-api-python-client==2.55.0
django-rq==2.5.1 django-rq==2.5.1
django-redis==5.2.0 django-redis==5.2.0
channels==4.0.0
channels-redis==4.0.0
uvicorn==0.20.0 uvicorn==0.20.0

View File

@ -68,21 +68,6 @@ services:
env_file: env_file:
- ./apiserver/.env - ./apiserver/.env
plane-channel-worker:
image: plane-api
container_name: plane-channel-worker
restart: always
depends_on:
- redis
- db
- plane-api
command: ./bin/channel-worker
links:
- redis:redis
- db:db
env_file:
- ./apiserver/.env
volumes: volumes:
pgdata: pgdata:
redisdata: redisdata: