mirror of https://github.com/jumpserver/jumpserver
feat: 优化工单模块 (#5361)
* feat: 优化工单模块1 * feat: 优化工单模块2 * feat: 优化工单模块3 Co-authored-by: Bai <bugatti_it@163.com>pull/5364/head
parent
3b056ff953
commit
430e20a49c
|
@ -203,14 +203,14 @@ class AuthMixin:
|
||||||
raise errors.LoginConfirmOtherError('', "Not found")
|
raise errors.LoginConfirmOtherError('', "Not found")
|
||||||
if ticket.status_open:
|
if ticket.status_open:
|
||||||
raise errors.LoginConfirmWaitError(ticket.id)
|
raise errors.LoginConfirmWaitError(ticket.id)
|
||||||
elif ticket.is_approved:
|
elif ticket.action_approve:
|
||||||
self.request.session["auth_confirm"] = "1"
|
self.request.session["auth_confirm"] = "1"
|
||||||
return
|
return
|
||||||
elif ticket.is_rejected:
|
elif ticket.action_reject:
|
||||||
raise errors.LoginConfirmOtherError(
|
raise errors.LoginConfirmOtherError(
|
||||||
ticket.id, ticket.get_action_display()
|
ticket.id, ticket.get_action_display()
|
||||||
)
|
)
|
||||||
elif ticket.is_closed:
|
elif ticket.action_close:
|
||||||
raise errors.LoginConfirmOtherError(
|
raise errors.LoginConfirmOtherError(
|
||||||
ticket.id, ticket.get_action_display()
|
ticket.id, ticket.get_action_display()
|
||||||
)
|
)
|
||||||
|
|
|
@ -71,7 +71,7 @@ class Action:
|
||||||
@classmethod
|
@classmethod
|
||||||
def value_to_choices_display(cls, value):
|
def value_to_choices_display(cls, value):
|
||||||
choices = cls.value_to_choices(value)
|
choices = cls.value_to_choices(value)
|
||||||
return [dict(cls.choices())[i] for i in choices]
|
return [str(dict(cls.choices())[i]) for i in choices]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def choices_to_value(cls, value):
|
def choices_to_value(cls, value):
|
||||||
|
|
|
@ -7,15 +7,15 @@ __all__ = ['TicketMetaSerializerViewMixin']
|
||||||
|
|
||||||
class TicketMetaSerializerViewMixin:
|
class TicketMetaSerializerViewMixin:
|
||||||
apply_asset_meta_serializer_classes = {
|
apply_asset_meta_serializer_classes = {
|
||||||
'apply': serializers.TicketMetaApplyAssetApplySerializer,
|
'open': serializers.TicketMetaApplyAssetApplySerializer,
|
||||||
'approve': serializers.TicketMetaApplyAssetApproveSerializer,
|
'approve': serializers.TicketMetaApplyAssetApproveSerializer,
|
||||||
}
|
}
|
||||||
apply_application_meta_serializer_classes = {
|
apply_application_meta_serializer_classes = {
|
||||||
'apply': serializers.TicketMetaApplyApplicationApplySerializer,
|
'open': serializers.TicketMetaApplyApplicationApplySerializer,
|
||||||
'approve': serializers.TicketMetaApplyApplicationApproveSerializer,
|
'approve': serializers.TicketMetaApplyApplicationApproveSerializer,
|
||||||
}
|
}
|
||||||
login_confirm_meta_serializer_classes = {
|
login_confirm_meta_serializer_classes = {
|
||||||
'apply': serializers.TicketMetaLoginConfirmApplySerializer,
|
'open': serializers.TicketMetaLoginConfirmApplySerializer,
|
||||||
}
|
}
|
||||||
meta_serializer_classes = {
|
meta_serializer_classes = {
|
||||||
const.TicketTypeChoices.apply_asset.value: apply_asset_meta_serializer_classes,
|
const.TicketTypeChoices.apply_asset.value: apply_asset_meta_serializer_classes,
|
||||||
|
@ -37,7 +37,7 @@ class TicketMetaSerializerViewMixin:
|
||||||
return meta_class
|
return meta_class
|
||||||
|
|
||||||
def get_serializer_meta_field(self):
|
def get_serializer_meta_field(self):
|
||||||
if self.action not in ['apply', 'approve']:
|
if self.action not in ['open', 'approve']:
|
||||||
return None
|
return None
|
||||||
meta_class = self.get_serializer_meta_field_class()
|
meta_class = self.get_serializer_meta_field_class()
|
||||||
if not meta_class:
|
if not meta_class:
|
||||||
|
|
|
@ -23,7 +23,7 @@ class TicketViewSet(TicketMetaSerializerViewMixin, CommonApiMixin, viewsets.Mode
|
||||||
serializer_classes = {
|
serializer_classes = {
|
||||||
'default': serializers.TicketDisplaySerializer,
|
'default': serializers.TicketDisplaySerializer,
|
||||||
'display': serializers.TicketDisplaySerializer,
|
'display': serializers.TicketDisplaySerializer,
|
||||||
'apply': serializers.TicketApplySerializer,
|
'open': serializers.TicketApplySerializer,
|
||||||
'approve': serializers.TicketApproveSerializer,
|
'approve': serializers.TicketApproveSerializer,
|
||||||
'reject': serializers.TicketRejectSerializer,
|
'reject': serializers.TicketRejectSerializer,
|
||||||
'close': serializers.TicketCloseSerializer,
|
'close': serializers.TicketCloseSerializer,
|
||||||
|
@ -50,7 +50,7 @@ class TicketViewSet(TicketMetaSerializerViewMixin, CommonApiMixin, viewsets.Mode
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
@action(detail=False, methods=[POST])
|
@action(detail=False, methods=[POST])
|
||||||
def apply(self, request, *args, **kwargs):
|
def open(self, request, *args, **kwargs):
|
||||||
return super().create(request, *args, **kwargs)
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
@action(detail=True, methods=[PUT], permission_classes=[IsOrgAdmin, IsAssignee, NotClosed])
|
@action(detail=True, methods=[PUT], permission_classes=[IsOrgAdmin, IsAssignee, NotClosed])
|
||||||
|
|
|
@ -16,7 +16,7 @@ class TicketTypeChoices(TextChoices):
|
||||||
|
|
||||||
|
|
||||||
class TicketActionChoices(TextChoices):
|
class TicketActionChoices(TextChoices):
|
||||||
apply = 'apply', _('Apply')
|
open = 'open', _('Open')
|
||||||
approve = 'approve', _('Approve')
|
approve = 'approve', _('Approve')
|
||||||
reject = 'reject', _('Reject')
|
reject = 'reject', _('Reject')
|
||||||
close = 'close', _('Close')
|
close = 'close', _('Close')
|
||||||
|
|
|
@ -24,19 +24,23 @@ def migrate_field_meta(tp, old_meta):
|
||||||
'apply_hostname_group': [old_meta_hostname] if old_meta_hostname else [],
|
'apply_hostname_group': [old_meta_hostname] if old_meta_hostname else [],
|
||||||
'apply_system_user_group': [old_meta_system_user] if old_meta_system_user else [],
|
'apply_system_user_group': [old_meta_system_user] if old_meta_system_user else [],
|
||||||
'apply_actions': old_meta.get('actions'),
|
'apply_actions': old_meta.get('actions'),
|
||||||
|
'apply_actions_display': [],
|
||||||
'apply_date_start': old_meta.get('date_start'),
|
'apply_date_start': old_meta.get('date_start'),
|
||||||
'apply_date_expired': old_meta.get('date_expired'),
|
'apply_date_expired': old_meta.get('date_expired'),
|
||||||
|
|
||||||
'approve_assets': old_meta.get('confirmed_assets', []),
|
'approve_assets': old_meta.get('confirmed_assets', []),
|
||||||
|
'approve_assets_snapshot': [],
|
||||||
'approve_system_users': old_meta.get('confirmed_system_users', []),
|
'approve_system_users': old_meta.get('confirmed_system_users', []),
|
||||||
|
'approve_system_users_snapshot': [],
|
||||||
'approve_actions': old_meta.get('actions'),
|
'approve_actions': old_meta.get('actions'),
|
||||||
|
'approve_actions_display': [],
|
||||||
'approve_date_start': old_meta.get('date_start'),
|
'approve_date_start': old_meta.get('date_start'),
|
||||||
'approve_date_expired': old_meta.get('date_expired'),
|
'approve_date_expired': old_meta.get('date_expired'),
|
||||||
}
|
}
|
||||||
return new_meta
|
return new_meta
|
||||||
|
|
||||||
|
|
||||||
ACTION_APPLY = 'apply'
|
ACTION_OPEN = 'open'
|
||||||
ACTION_CLOSE = 'close'
|
ACTION_CLOSE = 'close'
|
||||||
STATUS_OPEN = 'open'
|
STATUS_OPEN = 'open'
|
||||||
STATUS_CLOSED = 'closed'
|
STATUS_CLOSED = 'closed'
|
||||||
|
@ -46,7 +50,7 @@ def migrate_field_action(old_action, old_status):
|
||||||
if old_action:
|
if old_action:
|
||||||
return old_action
|
return old_action
|
||||||
if old_status == STATUS_OPEN:
|
if old_status == STATUS_OPEN:
|
||||||
return ACTION_APPLY
|
return ACTION_OPEN
|
||||||
if old_status == STATUS_CLOSED:
|
if old_status == STATUS_CLOSED:
|
||||||
return ACTION_CLOSE
|
return ACTION_CLOSE
|
||||||
|
|
||||||
|
@ -119,7 +123,8 @@ class Migration(migrations.Migration):
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='ticket',
|
model_name='ticket',
|
||||||
name='action',
|
name='action',
|
||||||
field=models.CharField(choices=[('apply', 'Apply'), ('approve', 'Approve'), ('reject', 'Reject'), ('close', 'Close')], default='apply', max_length=16, verbose_name='Action')),
|
field=models.CharField(choices=[('open', 'Open'), ('approve', 'Approve'), ('reject', 'Reject'), ('close', 'Close')], default='open', max_length=16, verbose_name='Action'),
|
||||||
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='ticket',
|
model_name='ticket',
|
||||||
name='status',
|
name='status',
|
||||||
|
|
|
@ -21,3 +21,6 @@ class Comment(CommonModelMixin):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('date_created', )
|
ordering = ('date_created', )
|
||||||
|
|
||||||
|
def set_display_fields(self):
|
||||||
|
self.user_display = str(self.user)
|
||||||
|
|
|
@ -2,6 +2,32 @@ import textwrap
|
||||||
from django.utils.translation import ugettext as __
|
from django.utils.translation import ugettext as __
|
||||||
|
|
||||||
|
|
||||||
|
class SetDisplayFieldMixin:
|
||||||
|
|
||||||
|
def set_applicant_display(self):
|
||||||
|
if self.has_applied:
|
||||||
|
self.applicant_display = str(self.applicant)
|
||||||
|
|
||||||
|
def set_assignees_display(self):
|
||||||
|
if self.has_applied:
|
||||||
|
assignees_display = [str(assignee) for assignee in self.assignees.all()]
|
||||||
|
self.assignees_display = ', '.join(assignees_display)
|
||||||
|
|
||||||
|
def set_processor_display(self):
|
||||||
|
if self.has_processed:
|
||||||
|
self.processor_display = str(self.processor)
|
||||||
|
|
||||||
|
def set_meta_display(self):
|
||||||
|
method_name = f'construct_meta_{self.type}_{self.action}_fields_display'
|
||||||
|
meta_display = getattr(self, method_name, lambda: {})()
|
||||||
|
self.meta.update(meta_display)
|
||||||
|
|
||||||
|
def set_display_fields(self):
|
||||||
|
self.set_applicant_display()
|
||||||
|
self.set_processor_display()
|
||||||
|
self.set_meta_display()
|
||||||
|
|
||||||
|
|
||||||
class ConstructBodyMixin:
|
class ConstructBodyMixin:
|
||||||
# applied body
|
# applied body
|
||||||
def construct_applied_body(self):
|
def construct_applied_body(self):
|
||||||
|
@ -32,7 +58,7 @@ class ConstructBodyMixin:
|
||||||
# meta body
|
# meta body
|
||||||
def construct_meta_body(self):
|
def construct_meta_body(self):
|
||||||
applied_body = self.construct_applied_body()
|
applied_body = self.construct_applied_body()
|
||||||
if not self.is_approved:
|
if not self.action_approve:
|
||||||
return applied_body
|
return applied_body
|
||||||
approved_body = self.construct_approved_body()
|
approved_body = self.construct_approved_body()
|
||||||
return applied_body + approved_body
|
return applied_body + approved_body
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
from .meta import *
|
|
@ -3,15 +3,46 @@ from orgs.utils import tmp_to_org, tmp_to_root_org
|
||||||
from applications.models import Application, Category
|
from applications.models import Application, Category
|
||||||
from assets.models import SystemUser
|
from assets.models import SystemUser
|
||||||
from perms.models import ApplicationPermission
|
from perms.models import ApplicationPermission
|
||||||
|
from tickets.utils import convert_model_data_field_name_to_verbose_name
|
||||||
|
|
||||||
|
|
||||||
|
class ConstructDisplayFieldMixin:
|
||||||
|
def construct_meta_apply_application_open_fields_display(self):
|
||||||
|
meta_display_fields = ['apply_category_display', 'apply_type_display']
|
||||||
|
apply_category = self.meta['apply_category']
|
||||||
|
apply_category_display = dict(Category.choices)[apply_category]
|
||||||
|
apply_type = self.meta['apply_type']
|
||||||
|
apply_type_display = dict(Category.get_type_choices(apply_category))[apply_type]
|
||||||
|
meta_display_values = [apply_category_display, apply_type_display]
|
||||||
|
meta_display = dict(zip(meta_display_fields, meta_display_values))
|
||||||
|
return meta_display
|
||||||
|
|
||||||
|
def construct_meta_apply_application_approve_fields_display(self):
|
||||||
|
meta_display_fields = ['approve_applications_snapshot', 'approve_system_users_snapshot']
|
||||||
|
approve_applications_id = self.meta['approve_applications']
|
||||||
|
approve_system_users_id = self.meta['approve_system_users']
|
||||||
|
with tmp_to_org(self.org_id):
|
||||||
|
approve_applications_snapshot = list(
|
||||||
|
Application.objects.filter(id__in=approve_applications_id).values(
|
||||||
|
'name', 'category', 'type'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
approve_system_users_snapshot = list(
|
||||||
|
SystemUser.objects.filter(id__in=approve_system_users_id).values(
|
||||||
|
'name', 'username', 'username_same_with_user', 'protocol',
|
||||||
|
'auto_push', 'sudo', 'home', 'sftp_root'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
meta_display_values = [approve_applications_snapshot, approve_system_users_snapshot]
|
||||||
|
meta_display = dict(zip(meta_display_fields, meta_display_values))
|
||||||
|
return meta_display
|
||||||
|
|
||||||
|
|
||||||
class ConstructBodyMixin:
|
class ConstructBodyMixin:
|
||||||
|
|
||||||
def construct_apply_application_applied_body(self):
|
def construct_apply_application_applied_body(self):
|
||||||
apply_category = self.meta['apply_category']
|
apply_category_display = self.meta['apply_category_display']
|
||||||
apply_category_display = dict(Category.choices)[apply_category]
|
apply_type_display = self.meta['apply_type_display']
|
||||||
apply_type = self.meta['apply_type']
|
|
||||||
apply_type_display = dict(Category.get_type_choices(apply_category))[apply_type]
|
|
||||||
apply_application_group = self.meta['apply_application_group']
|
apply_application_group = self.meta['apply_application_group']
|
||||||
apply_system_user_group = self.meta['apply_system_user_group']
|
apply_system_user_group = self.meta['apply_system_user_group']
|
||||||
apply_date_start = self.meta['apply_date_start']
|
apply_date_start = self.meta['apply_date_start']
|
||||||
|
@ -34,13 +65,14 @@ class ConstructBodyMixin:
|
||||||
|
|
||||||
def construct_apply_application_approved_body(self):
|
def construct_apply_application_approved_body(self):
|
||||||
# 审批信息
|
# 审批信息
|
||||||
approve_applications_id = self.meta['approve_applications']
|
approve_applications_snapshot = self.meta['approve_applications_snapshot']
|
||||||
approve_system_users_id = self.meta['approve_system_users']
|
approve_applications_snapshot_display = convert_model_data_field_name_to_verbose_name(
|
||||||
with tmp_to_org(self.org_id):
|
Application, approve_applications_snapshot
|
||||||
approve_applications = Application.objects.filter(id__in=approve_applications_id)
|
)
|
||||||
approve_system_users = SystemUser.objects.filter(id__in=approve_system_users_id)
|
approve_system_users_snapshot = self.meta['approve_system_users_snapshot']
|
||||||
approve_applications_display = [str(application) for application in approve_applications]
|
approve_system_users_snapshot_display = convert_model_data_field_name_to_verbose_name(
|
||||||
approve_system_users_display = [str(system_user) for system_user in approve_system_users]
|
SystemUser, approve_system_users_snapshot
|
||||||
|
)
|
||||||
approve_date_start = self.meta['approve_date_start']
|
approve_date_start = self.meta['approve_date_start']
|
||||||
approve_date_expired = self.meta['approve_date_expired']
|
approve_date_expired = self.meta['approve_date_expired']
|
||||||
approved_body = '''{}: {},
|
approved_body = '''{}: {},
|
||||||
|
@ -48,8 +80,8 @@ class ConstructBodyMixin:
|
||||||
{}: {},
|
{}: {},
|
||||||
{}: {},
|
{}: {},
|
||||||
'''.format(
|
'''.format(
|
||||||
__('Approved applications'), ', '.join(approve_applications_display),
|
__('Approved applications'), approve_applications_snapshot_display,
|
||||||
__('Approved system users'), ', '.join(approve_system_users_display),
|
__('Approved system users'), approve_system_users_snapshot_display,
|
||||||
__('Approved date start'), approve_date_start,
|
__('Approved date start'), approve_date_start,
|
||||||
__('Approved date expired'), approve_date_expired
|
__('Approved date expired'), approve_date_expired
|
||||||
)
|
)
|
|
@ -3,6 +3,45 @@ from django.utils.translation import ugettext as __
|
||||||
from perms.models import AssetPermission, Action
|
from perms.models import AssetPermission, Action
|
||||||
from assets.models import Asset, SystemUser
|
from assets.models import Asset, SystemUser
|
||||||
from orgs.utils import tmp_to_org, tmp_to_root_org
|
from orgs.utils import tmp_to_org, tmp_to_root_org
|
||||||
|
from tickets.utils import convert_model_data_field_name_to_verbose_name
|
||||||
|
|
||||||
|
|
||||||
|
class ConstructDisplayFieldMixin:
|
||||||
|
def construct_meta_apply_asset_open_fields_display(self):
|
||||||
|
meta_display_fields = ['apply_actions_display']
|
||||||
|
|
||||||
|
apply_actions = self.meta['apply_actions']
|
||||||
|
apply_actions_display = Action.value_to_choices_display(apply_actions)
|
||||||
|
|
||||||
|
meta_display_values = [apply_actions_display]
|
||||||
|
meta_display = dict(zip(meta_display_fields, meta_display_values))
|
||||||
|
return meta_display
|
||||||
|
|
||||||
|
def construct_meta_apply_asset_approve_fields_display(self):
|
||||||
|
meta_display_fields = [
|
||||||
|
'approve_actions_display', 'approve_assets_snapshot', 'approve_system_users_snapshot'
|
||||||
|
]
|
||||||
|
approve_actions = self.meta['approve_actions']
|
||||||
|
approve_assets_id = self.meta['approve_assets']
|
||||||
|
approve_system_users_id = self.meta['approve_system_users']
|
||||||
|
approve_actions_display = Action.value_to_choices_display(approve_actions)
|
||||||
|
with tmp_to_org(self.org_id):
|
||||||
|
approve_assets_snapshot = list(
|
||||||
|
Asset.objects.filter(id__in=approve_assets_id).values(
|
||||||
|
'hostname', 'ip', 'protocols', 'platform__name', 'public_ip'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
approve_system_users_snapshot = list(
|
||||||
|
SystemUser.objects.filter(id__in=approve_system_users_id).values(
|
||||||
|
'name', 'username', 'username_same_with_user', 'protocol',
|
||||||
|
'auto_push', 'sudo', 'home', 'sftp_root'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
meta_display_values = [
|
||||||
|
approve_actions_display, approve_assets_snapshot, approve_system_users_snapshot
|
||||||
|
]
|
||||||
|
meta_display = dict(zip(meta_display_fields, meta_display_values))
|
||||||
|
return meta_display
|
||||||
|
|
||||||
|
|
||||||
class ConstructBodyMixin:
|
class ConstructBodyMixin:
|
||||||
|
@ -10,9 +49,7 @@ class ConstructBodyMixin:
|
||||||
apply_ip_group = self.meta['apply_ip_group']
|
apply_ip_group = self.meta['apply_ip_group']
|
||||||
apply_hostname_group = self.meta['apply_hostname_group']
|
apply_hostname_group = self.meta['apply_hostname_group']
|
||||||
apply_system_user_group = self.meta['apply_system_user_group']
|
apply_system_user_group = self.meta['apply_system_user_group']
|
||||||
apply_actions = self.meta['apply_actions']
|
apply_actions_display = self.meta['apply_actions_display']
|
||||||
apply_actions_display = Action.value_to_choices_display(apply_actions)
|
|
||||||
apply_actions_display = [str(action_display) for action_display in apply_actions_display]
|
|
||||||
apply_date_start = self.meta['apply_date_start']
|
apply_date_start = self.meta['apply_date_start']
|
||||||
apply_date_expired = self.meta['apply_date_expired']
|
apply_date_expired = self.meta['apply_date_expired']
|
||||||
applied_body = '''{}: {},
|
applied_body = '''{}: {},
|
||||||
|
@ -31,16 +68,15 @@ class ConstructBodyMixin:
|
||||||
return applied_body
|
return applied_body
|
||||||
|
|
||||||
def construct_apply_asset_approved_body(self):
|
def construct_apply_asset_approved_body(self):
|
||||||
approve_assets_id = self.meta['approve_assets']
|
approve_assets_snapshot = self.meta['approve_assets_snapshot']
|
||||||
approve_system_users_id = self.meta['approve_system_users']
|
approve_assets_snapshot_display = convert_model_data_field_name_to_verbose_name(
|
||||||
with tmp_to_org(self.org_id):
|
Asset, approve_assets_snapshot
|
||||||
approve_assets = Asset.objects.filter(id__in=approve_assets_id)
|
)
|
||||||
approve_system_users = SystemUser.objects.filter(id__in=approve_system_users_id)
|
approve_system_users_snapshot = self.meta['approve_system_users_snapshot']
|
||||||
approve_assets_display = [str(asset) for asset in approve_assets]
|
approve_system_users_snapshot_display = convert_model_data_field_name_to_verbose_name(
|
||||||
approve_system_users_display = [str(system_user) for system_user in approve_system_users]
|
SystemUser, approve_system_users_snapshot
|
||||||
approve_actions = self.meta['approve_actions']
|
)
|
||||||
approve_actions_display = Action.value_to_choices_display(approve_actions)
|
approve_actions_display = self.meta['approve_actions_display']
|
||||||
approve_actions_display = [str(action_display) for action_display in approve_actions_display]
|
|
||||||
approve_date_start = self.meta['approve_date_start']
|
approve_date_start = self.meta['approve_date_start']
|
||||||
approve_date_expired = self.meta['approve_date_expired']
|
approve_date_expired = self.meta['approve_date_expired']
|
||||||
approved_body = '''{}: {},
|
approved_body = '''{}: {},
|
||||||
|
@ -49,8 +85,8 @@ class ConstructBodyMixin:
|
||||||
{}: {},
|
{}: {},
|
||||||
{}: {}
|
{}: {}
|
||||||
'''.format(
|
'''.format(
|
||||||
__('Approved assets'), ', '.join(approve_assets_display),
|
__('Approved assets'), approve_assets_snapshot_display,
|
||||||
__('Approved system users'), ', '.join(approve_system_users_display),
|
__('Approved system users'), approve_system_users_snapshot_display,
|
||||||
__('Approved actions'), ', '.join(approve_actions_display),
|
__('Approved actions'), ', '.join(approve_actions_display),
|
||||||
__('Approved date start'), approve_date_start,
|
__('Approved date start'), approve_date_start,
|
||||||
__('Approved date expired'), approve_date_expired,
|
__('Approved date expired'), approve_date_expired,
|
|
@ -0,0 +1,35 @@
|
||||||
|
from . import apply_asset, apply_application, login_confirm
|
||||||
|
|
||||||
|
__all__ = ['ConstructDisplayFieldMixin', 'ConstructBodyMixin', 'CreatePermissionMixin']
|
||||||
|
|
||||||
|
|
||||||
|
modules = (apply_asset, apply_application, login_confirm)
|
||||||
|
|
||||||
|
|
||||||
|
construct_display_field_mixin_cls_name = 'ConstructDisplayFieldMixin'
|
||||||
|
construct_body_mixin_cls_name = 'ConstructBodyMixin'
|
||||||
|
create_permission_mixin_cls_name = 'CreatePermissionMixin'
|
||||||
|
|
||||||
|
|
||||||
|
def get_mixin_base_cls_list(base_cls_name):
|
||||||
|
return [
|
||||||
|
getattr(module, base_cls_name) for module in modules if hasattr(module, base_cls_name)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ConstructDisplayFieldMixin(
|
||||||
|
*get_mixin_base_cls_list(construct_display_field_mixin_cls_name)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ConstructBodyMixin(
|
||||||
|
*get_mixin_base_cls_list(construct_body_mixin_cls_name)
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CreatePermissionMixin(
|
||||||
|
*get_mixin_base_cls_list(create_permission_mixin_cls_name)
|
||||||
|
):
|
||||||
|
pass
|
|
@ -1,32 +1,30 @@
|
||||||
from . import base, apply_asset, apply_application, login_confirm
|
from . import base, meta
|
||||||
|
|
||||||
__all__ = ['TicketModelMixin']
|
__all__ = ['TicketModelMixin']
|
||||||
|
|
||||||
|
|
||||||
class TicketConstructBodyMixin(
|
class TicketSetDisplayFieldMixin(meta.ConstructDisplayFieldMixin, base.SetDisplayFieldMixin):
|
||||||
base.ConstructBodyMixin,
|
""" 设置 ticket display 字段(只设置,不保存)"""
|
||||||
apply_asset.ConstructBodyMixin,
|
|
||||||
apply_application.ConstructBodyMixin,
|
|
||||||
login_confirm.ConstructBodyMixin
|
|
||||||
):
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TicketCreatePermissionMixin(
|
class TicketConstructBodyMixin(meta.ConstructBodyMixin, base.ConstructBodyMixin):
|
||||||
base.CreatePermissionMixin,
|
""" 构造 ticket body 信息 """
|
||||||
apply_asset.CreatePermissionMixin,
|
|
||||||
apply_application.CreatePermissionMixin
|
|
||||||
):
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TicketCreateCommentMixin(
|
class TicketCreatePermissionMixin(meta.CreatePermissionMixin, base.CreatePermissionMixin):
|
||||||
base.CreateCommentMixin
|
""" 创建 ticket 相关授权规则"""
|
||||||
):
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TicketCreateCommentMixin(base.CreateCommentMixin):
|
||||||
|
""" 创建 ticket 评论"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TicketModelMixin(
|
class TicketModelMixin(
|
||||||
TicketConstructBodyMixin, TicketCreatePermissionMixin, TicketCreateCommentMixin
|
TicketSetDisplayFieldMixin, TicketConstructBodyMixin, TicketCreatePermissionMixin,
|
||||||
|
TicketCreateCommentMixin
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -24,6 +24,8 @@ class ModelJSONFieldEncoder(json.JSONEncoder):
|
||||||
return obj.strftime(settings.DATETIME_DISPLAY_FORMAT)
|
return obj.strftime(settings.DATETIME_DISPLAY_FORMAT)
|
||||||
if isinstance(obj, uuid.UUID):
|
if isinstance(obj, uuid.UUID):
|
||||||
return str(obj)
|
return str(obj)
|
||||||
|
if isinstance(obj, type(_("ugettext_lazy"))):
|
||||||
|
return str(obj)
|
||||||
else:
|
else:
|
||||||
return super().default(obj)
|
return super().default(obj)
|
||||||
|
|
||||||
|
@ -37,7 +39,7 @@ class Ticket(TicketModelMixin, CommonModelMixin, OrgModelMixin):
|
||||||
meta = models.JSONField(encoder=ModelJSONFieldEncoder, verbose_name=_("Meta"))
|
meta = models.JSONField(encoder=ModelJSONFieldEncoder, verbose_name=_("Meta"))
|
||||||
action = models.CharField(
|
action = models.CharField(
|
||||||
choices=const.TicketActionChoices.choices, max_length=16,
|
choices=const.TicketActionChoices.choices, max_length=16,
|
||||||
default=const.TicketActionChoices.apply.value, verbose_name=_("Action")
|
default=const.TicketActionChoices.open.value, verbose_name=_("Action")
|
||||||
)
|
)
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=16, choices=const.TicketStatusChoices.choices,
|
max_length=16, choices=const.TicketStatusChoices.choices,
|
||||||
|
@ -75,9 +77,18 @@ class Ticket(TicketModelMixin, CommonModelMixin, OrgModelMixin):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{}({})'.format(self.title, self.applicant_display)
|
return '{}({})'.format(self.title, self.applicant_display)
|
||||||
|
|
||||||
|
# type
|
||||||
|
@property
|
||||||
|
def type_apply_asset(self):
|
||||||
|
return self.type == const.TicketTypeChoices.apply_asset.value
|
||||||
|
|
||||||
def has_assignee(self, assignee):
|
@property
|
||||||
return self.assignees.filter(id=assignee.id).exists()
|
def type_apply_application(self):
|
||||||
|
return self.type == const.TicketTypeChoices.apply_application.value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type_login_confirm(self):
|
||||||
|
return self.type == const.TicketTypeChoices.login_confirm.value
|
||||||
|
|
||||||
# status
|
# status
|
||||||
@property
|
@property
|
||||||
|
@ -88,34 +99,46 @@ class Ticket(TicketModelMixin, CommonModelMixin, OrgModelMixin):
|
||||||
def status_open(self):
|
def status_open(self):
|
||||||
return self.status == const.TicketStatusChoices.open.value
|
return self.status == const.TicketStatusChoices.open.value
|
||||||
|
|
||||||
|
def set_status_closed(self):
|
||||||
|
self.status = const.TicketStatusChoices.closed.value
|
||||||
|
|
||||||
# action
|
# action
|
||||||
@property
|
@property
|
||||||
def is_applied(self):
|
def action_open(self):
|
||||||
return self.action == const.TicketActionChoices.apply.value
|
return self.action == const.TicketActionChoices.open.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_approved(self):
|
def action_approve(self):
|
||||||
return self.action == const.TicketActionChoices.approve.value
|
return self.action == const.TicketActionChoices.approve.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_rejected(self):
|
def action_reject(self):
|
||||||
return self.action == const.TicketActionChoices.reject.value
|
return self.action == const.TicketActionChoices.reject.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_closed(self):
|
def action_close(self):
|
||||||
return self.action == const.TicketActionChoices.close.value
|
return self.action == const.TicketActionChoices.close.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_processed(self):
|
def has_applied(self):
|
||||||
return self.is_approved or self.is_rejected or self.is_closed
|
return self.action_open
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_processed(self):
|
||||||
|
return self.action_approve or self.action_reject or self.action_close
|
||||||
|
|
||||||
|
def set_action_close(self):
|
||||||
|
self.action = const.TicketActionChoices.close.value
|
||||||
|
|
||||||
# perform action
|
|
||||||
def close(self, processor):
|
def close(self, processor):
|
||||||
self.processor = processor
|
self.processor = processor
|
||||||
self.action = const.TicketActionChoices.close.value
|
self.set_action_close()
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
# tickets
|
#
|
||||||
|
def has_assignee(self, assignee):
|
||||||
|
return self.assignees.filter(id=assignee.id).exists()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def all(cls):
|
def all(cls):
|
||||||
with tmp_to_root_org():
|
with tmp_to_root_org():
|
||||||
|
|
|
@ -9,4 +9,5 @@ class IsAssignee(permissions.BasePermission):
|
||||||
|
|
||||||
class NotClosed(permissions.BasePermission):
|
class NotClosed(permissions.BasePermission):
|
||||||
def has_object_permission(self, request, view, obj):
|
def has_object_permission(self, request, view, obj):
|
||||||
|
return True
|
||||||
return not obj.status_closed
|
return not obj.status_closed
|
||||||
|
|
|
@ -14,16 +14,24 @@ __all__ = [
|
||||||
class TicketMetaApplyApplicationSerializer(BaseTicketMetaSerializer):
|
class TicketMetaApplyApplicationSerializer(BaseTicketMetaSerializer):
|
||||||
# 申请信息
|
# 申请信息
|
||||||
apply_category = serializers.ChoiceField(
|
apply_category = serializers.ChoiceField(
|
||||||
choices=Category.choices, required=True, label=_('Category')
|
required=True, choices=Category.choices, label=_('Category')
|
||||||
|
)
|
||||||
|
apply_category_display = serializers.CharField(
|
||||||
|
read_only=True, label=_('Category display')
|
||||||
)
|
)
|
||||||
apply_type = serializers.ChoiceField(
|
apply_type = serializers.ChoiceField(
|
||||||
choices=Category.get_all_type_choices(), required=True, label=_('Type')
|
required=True, choices=Category.get_all_type_choices(), label=_('Type')
|
||||||
|
)
|
||||||
|
apply_type_display = serializers.CharField(
|
||||||
|
required=False, read_only=True, label=_('Type display')
|
||||||
)
|
)
|
||||||
apply_application_group = serializers.ListField(
|
apply_application_group = serializers.ListField(
|
||||||
child=serializers.CharField(), default=list, label=_('Application group')
|
required=False, child=serializers.CharField(), label=_('Application group'),
|
||||||
|
default=list,
|
||||||
)
|
)
|
||||||
apply_system_user_group = serializers.ListField(
|
apply_system_user_group = serializers.ListField(
|
||||||
child=serializers.CharField(), default=list, label=_('System user group')
|
required=False, child=serializers.CharField(), label=_('System user group'),
|
||||||
|
default=list,
|
||||||
)
|
)
|
||||||
apply_date_start = serializers.DateTimeField(
|
apply_date_start = serializers.DateTimeField(
|
||||||
required=True, label=_('Date start')
|
required=True, label=_('Date start')
|
||||||
|
@ -33,12 +41,20 @@ class TicketMetaApplyApplicationSerializer(BaseTicketMetaSerializer):
|
||||||
)
|
)
|
||||||
# 审批信息
|
# 审批信息
|
||||||
approve_applications = serializers.ListField(
|
approve_applications = serializers.ListField(
|
||||||
child=serializers.UUIDField(), required=True,
|
required=True, child=serializers.UUIDField(), label=_('Approve applications')
|
||||||
label=_('Approve applications')
|
)
|
||||||
|
approve_applications_snapshot = serializers.ListField(
|
||||||
|
required=False, read_only=True, child=serializers.CharField(),
|
||||||
|
label=_('Approve applications display'),
|
||||||
|
default=list
|
||||||
)
|
)
|
||||||
approve_system_users = serializers.ListField(
|
approve_system_users = serializers.ListField(
|
||||||
child=serializers.UUIDField(), required=True,
|
required=True, child=serializers.UUIDField(), label=_('Approve system users')
|
||||||
label=_('Approve system users')
|
)
|
||||||
|
approve_system_users_snapshot = serializers.ListField(
|
||||||
|
required=False, read_only=True, child=serializers.CharField(),
|
||||||
|
label=_('Approve system user display'),
|
||||||
|
default=list
|
||||||
)
|
)
|
||||||
approve_date_start = serializers.DateTimeField(
|
approve_date_start = serializers.DateTimeField(
|
||||||
required=True, label=_('Date start')
|
required=True, label=_('Date start')
|
||||||
|
@ -52,9 +68,10 @@ class TicketMetaApplyApplicationApplySerializer(TicketMetaApplyApplicationSerial
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = [
|
fields = [
|
||||||
'apply_category', 'apply_type',
|
'apply_category', 'apply_category_display',
|
||||||
|
'apply_type', 'apply_type_display',
|
||||||
'apply_application_group', 'apply_system_user_group',
|
'apply_application_group', 'apply_system_user_group',
|
||||||
'apply_date_start', 'apply_date_expired'
|
'apply_date_start', 'apply_date_expired',
|
||||||
]
|
]
|
||||||
|
|
||||||
def validate_apply_type(self, tp):
|
def validate_apply_type(self, tp):
|
||||||
|
@ -73,7 +90,8 @@ class TicketMetaApplyApplicationApproveSerializer(BaseTicketMetaApproveSerialize
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = {
|
fields = {
|
||||||
'approve_applications', 'approve_system_users',
|
'approve_applications', 'approve_applications_snapshot',
|
||||||
|
'approve_system_users', 'approve_system_users_snapshot',
|
||||||
'approve_date_start', 'approve_date_expired'
|
'approve_date_start', 'approve_date_expired'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from perms.serializers import ActionsField
|
from perms.serializers import ActionsField
|
||||||
from perms.models import Action
|
|
||||||
from assets.models import Asset, SystemUser
|
from assets.models import Asset, SystemUser
|
||||||
from .base import BaseTicketMetaSerializer, BaseTicketMetaApproveSerializerMixin
|
from .base import BaseTicketMetaSerializer, BaseTicketMetaApproveSerializerMixin
|
||||||
|
|
||||||
|
@ -15,16 +14,24 @@ __all__ = [
|
||||||
class TicketMetaApplyAssetSerializer(BaseTicketMetaSerializer):
|
class TicketMetaApplyAssetSerializer(BaseTicketMetaSerializer):
|
||||||
# 申请信息
|
# 申请信息
|
||||||
apply_ip_group = serializers.ListField(
|
apply_ip_group = serializers.ListField(
|
||||||
child=serializers.IPAddressField(), default=list, label=_('IP group')
|
required=False, child=serializers.IPAddressField(), label=_('IP group'),
|
||||||
|
default=list,
|
||||||
)
|
)
|
||||||
apply_hostname_group = serializers.ListField(
|
apply_hostname_group = serializers.ListField(
|
||||||
child=serializers.CharField(), default=list, label=_('Hostname group')
|
required=False, child=serializers.CharField(), label=_('Hostname group'),
|
||||||
|
default=list,
|
||||||
)
|
)
|
||||||
apply_system_user_group = serializers.ListField(
|
apply_system_user_group = serializers.ListField(
|
||||||
child=serializers.CharField(), default=list, label=_('System user group')
|
required=False, child=serializers.CharField(), label=_('System user group'),
|
||||||
|
default=list,
|
||||||
)
|
)
|
||||||
apply_actions = ActionsField(
|
apply_actions = ActionsField(
|
||||||
choices=Action.DB_CHOICES, default=Action.ALL
|
required=True
|
||||||
|
)
|
||||||
|
apply_actions_display = serializers.ListField(
|
||||||
|
required=False, read_only=True, child=serializers.CharField(),
|
||||||
|
label=_('Approve assets display'),
|
||||||
|
default=list,
|
||||||
)
|
)
|
||||||
apply_date_start = serializers.DateTimeField(
|
apply_date_start = serializers.DateTimeField(
|
||||||
required=True, label=_('Date start')
|
required=True, label=_('Date start')
|
||||||
|
@ -36,11 +43,26 @@ class TicketMetaApplyAssetSerializer(BaseTicketMetaSerializer):
|
||||||
approve_assets = serializers.ListField(
|
approve_assets = serializers.ListField(
|
||||||
required=True, child=serializers.UUIDField(), label=_('Approve assets')
|
required=True, child=serializers.UUIDField(), label=_('Approve assets')
|
||||||
)
|
)
|
||||||
|
approve_assets_snapshot = serializers.ListField(
|
||||||
|
required=False, read_only=True, child=serializers.DictField(),
|
||||||
|
label=_('Approve assets display'),
|
||||||
|
default=list,
|
||||||
|
)
|
||||||
approve_system_users = serializers.ListField(
|
approve_system_users = serializers.ListField(
|
||||||
required=True, child=serializers.UUIDField(), label=_('Approve system users')
|
required=True, child=serializers.UUIDField(), label=_('Approve system users')
|
||||||
)
|
)
|
||||||
|
approve_system_users_snapshot = serializers.ListField(
|
||||||
|
required=False, read_only=True, child=serializers.DictField(),
|
||||||
|
label=_('Approve assets display'),
|
||||||
|
default=list,
|
||||||
|
)
|
||||||
approve_actions = ActionsField(
|
approve_actions = ActionsField(
|
||||||
required=False, choices=Action.DB_CHOICES, default=Action.ALL
|
required=True
|
||||||
|
)
|
||||||
|
approve_actions_display = serializers.ListField(
|
||||||
|
required=False, read_only=True, child=serializers.CharField(),
|
||||||
|
label=_('Approve assets display'),
|
||||||
|
default=list,
|
||||||
)
|
)
|
||||||
approve_date_start = serializers.DateTimeField(
|
approve_date_start = serializers.DateTimeField(
|
||||||
required=True, label=_('Date start')
|
required=True, label=_('Date start')
|
||||||
|
@ -54,9 +76,10 @@ class TicketMetaApplyAssetApplySerializer(TicketMetaApplyAssetSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = [
|
fields = [
|
||||||
'apply_ip_group', 'apply_hostname_group',
|
'apply_ip_group',
|
||||||
'apply_system_user_group', 'apply_actions',
|
'apply_hostname_group', 'apply_system_user_group',
|
||||||
'apply_date_start', 'apply_date_expired'
|
'apply_actions', 'apply_actions_display',
|
||||||
|
'apply_date_start', 'apply_date_expired',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,9 +88,10 @@ class TicketMetaApplyAssetApproveSerializer(BaseTicketMetaApproveSerializerMixin
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = [
|
fields = [
|
||||||
'approve_assets', 'approve_system_users',
|
'approve_assets', 'approve_assets_snapshot',
|
||||||
'approve_actions', 'approve_date_start',
|
'approve_system_users', 'approve_system_users_snapshot',
|
||||||
'approve_date_expired'
|
'approve_actions', 'approve_actions_display',
|
||||||
|
'approve_date_start', 'approve_date_expired',
|
||||||
]
|
]
|
||||||
|
|
||||||
def validate_approve_assets(self, approve_assets):
|
def validate_approve_assets(self, approve_assets):
|
||||||
|
|
|
@ -8,6 +8,9 @@ from assets.models import SystemUser
|
||||||
|
|
||||||
class BaseTicketMetaSerializer(serializers.Serializer):
|
class BaseTicketMetaSerializer(serializers.Serializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
def get_fields(self):
|
def get_fields(self):
|
||||||
fields = super().get_fields()
|
fields = super().get_fields()
|
||||||
required_fields = self.Meta.fields
|
required_fields = self.Meta.fields
|
||||||
|
@ -20,9 +23,6 @@ class BaseTicketMetaSerializer(serializers.Serializer):
|
||||||
})
|
})
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
class Meta:
|
|
||||||
fields = '__all__'
|
|
||||||
|
|
||||||
|
|
||||||
class BaseTicketMetaApproveSerializerMixin:
|
class BaseTicketMetaApproveSerializerMixin:
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ class TicketDisplaySerializer(TicketSerializer):
|
||||||
|
|
||||||
|
|
||||||
class TicketActionSerializer(TicketSerializer):
|
class TicketActionSerializer(TicketSerializer):
|
||||||
action = ReadableHiddenField(default=const.TicketActionChoices.apply.value)
|
action = ReadableHiddenField(default=const.TicketActionChoices.open.value)
|
||||||
|
|
||||||
class Meta(TicketSerializer.Meta):
|
class Meta(TicketSerializer.Meta):
|
||||||
required_fields = ['action']
|
required_fields = ['action']
|
||||||
|
@ -94,7 +94,7 @@ class TicketApplySerializer(TicketActionSerializer):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_action(action):
|
def validate_action(action):
|
||||||
return const.TicketActionChoices.apply.value
|
return const.TicketActionChoices.open.value
|
||||||
|
|
||||||
|
|
||||||
class TicketProcessSerializer(TicketActionSerializer):
|
class TicketProcessSerializer(TicketActionSerializer):
|
||||||
|
@ -110,13 +110,11 @@ class TicketApproveSerializer(TicketProcessSerializer):
|
||||||
class Meta(TicketProcessSerializer.Meta):
|
class Meta(TicketProcessSerializer.Meta):
|
||||||
required_fields = TicketProcessSerializer.Meta.required_fields + ['meta']
|
required_fields = TicketProcessSerializer.Meta.required_fields + ['meta']
|
||||||
read_only_fields = list(set(TicketDisplaySerializer.Meta.fields) - set(required_fields))
|
read_only_fields = list(set(TicketDisplaySerializer.Meta.fields) - set(required_fields))
|
||||||
extra_kwargs = {
|
|
||||||
'meta': {'read_only': True}
|
|
||||||
}
|
|
||||||
|
|
||||||
def validate_meta(self, meta):
|
def validate_meta(self, meta):
|
||||||
meta.update(self.instance.meta)
|
instance_meta = self.instance.meta
|
||||||
return meta
|
instance_meta.update(meta)
|
||||||
|
return instance_meta
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_action(action):
|
def validate_action(action):
|
||||||
|
|
|
@ -17,20 +17,18 @@ logger = get_logger(__name__)
|
||||||
|
|
||||||
@receiver(pre_save, sender=Ticket)
|
@receiver(pre_save, sender=Ticket)
|
||||||
def on_ticket_pre_save(sender, instance=None, **kwargs):
|
def on_ticket_pre_save(sender, instance=None, **kwargs):
|
||||||
if instance.is_applied:
|
if instance.has_processed:
|
||||||
instance.applicant_display = str(instance.applicant)
|
instance.set_status_closed()
|
||||||
if instance.is_processed:
|
instance.set_display_fields()
|
||||||
instance.processor_display = str(instance.processor)
|
|
||||||
instance.status = const.TicketStatusChoices.closed.value
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Ticket)
|
@receiver(post_save, sender=Ticket)
|
||||||
def on_ticket_processed(sender, instance=None, created=False, **kwargs):
|
def on_ticket_processed(sender, instance=None, **kwargs):
|
||||||
if not instance.is_processed:
|
if not instance.has_processed:
|
||||||
return
|
return
|
||||||
logger.debug('Ticket is processed, send mail: {}'.format(instance.id))
|
logger.debug('Ticket is processed, send mail: {}'.format(instance.id))
|
||||||
instance.create_action_comment()
|
instance.create_action_comment()
|
||||||
if instance.is_approved:
|
if instance.action_approve:
|
||||||
instance.create_permission()
|
instance.create_permission()
|
||||||
instance.create_approved_comment()
|
instance.create_approved_comment()
|
||||||
send_ticket_processed_mail_to_applicant(instance)
|
send_ticket_processed_mail_to_applicant(instance)
|
||||||
|
@ -43,18 +41,15 @@ def on_ticket_assignees_changed(sender, instance=None, action=None, reverse=Fals
|
||||||
if action != 'post_add':
|
if action != 'post_add':
|
||||||
return
|
return
|
||||||
ticket = instance
|
ticket = instance
|
||||||
assignees_display = [str(assignee) for assignee in ticket.assignees.all()]
|
logger.debug('Receives ticket and assignees changed signal, ticket: {}'.format(ticket.title))
|
||||||
logger.debug(
|
ticket.set_assignees_display()
|
||||||
'Receives ticket and assignees changed signal, ticket: {}, assignees: {}'
|
|
||||||
''.format(ticket.title, assignees_display)
|
|
||||||
)
|
|
||||||
ticket.assignees_display = ', '.join(assignees_display)
|
|
||||||
ticket.save()
|
ticket.save()
|
||||||
logger.debug('Send applied email to assignees: {}'.format(assignees_display))
|
|
||||||
assignees = model.objects.filter(pk__in=pk_set)
|
assignees = model.objects.filter(pk__in=pk_set)
|
||||||
|
assignees_display = [str(assignee) for assignee in assignees]
|
||||||
|
logger.debug('Send applied email to assignees: {}'.format(assignees_display))
|
||||||
send_ticket_applied_mail_to_assignees(ticket, assignees)
|
send_ticket_applied_mail_to_assignees(ticket, assignees)
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_save, sender=Comment)
|
@receiver(pre_save, sender=Comment)
|
||||||
def on_comment_create(sender, instance=None, created=False, **kwargs):
|
def on_comment_create(sender, instance=None, created=False, **kwargs):
|
||||||
instance.user_display = str(instance.user)
|
instance.set_display_fields()
|
||||||
|
|
|
@ -11,6 +11,32 @@ from . import const
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_model_data_field_name_to_verbose_name(model, name_data):
|
||||||
|
"""将Model以field_name为key的数据转换为以field_verbose_name为key的数据"""
|
||||||
|
if isinstance(name_data, dict):
|
||||||
|
name_data = [name_data]
|
||||||
|
|
||||||
|
model_fields_name_verbose_name_mapping = {
|
||||||
|
field.name: field.verbose_name for field in model._meta.fields
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_verbose_name(field_name):
|
||||||
|
verbose_name = model_fields_name_verbose_name_mapping.get(field_name)
|
||||||
|
if not verbose_name:
|
||||||
|
other_name = field_name.split('__', 1)[0]
|
||||||
|
verbose_name = model_fields_name_verbose_name_mapping.get(other_name)
|
||||||
|
if not verbose_name:
|
||||||
|
verbose_name = field_name
|
||||||
|
return verbose_name
|
||||||
|
|
||||||
|
verbose_name_data = [
|
||||||
|
{get_verbose_name(name): value for name, value in d.items()}
|
||||||
|
for d in name_data
|
||||||
|
]
|
||||||
|
|
||||||
|
return verbose_name_data
|
||||||
|
|
||||||
|
|
||||||
def send_ticket_applied_mail_to_assignees(ticket, assignees):
|
def send_ticket_applied_mail_to_assignees(ticket, assignees):
|
||||||
if not assignees:
|
if not assignees:
|
||||||
logger.debug("Not found assignees, ticket: {}({}), assignees: {}".format(
|
logger.debug("Not found assignees, ticket: {}({}), assignees: {}".format(
|
||||||
|
|
Loading…
Reference in New Issue