From 772e490afa8380792878b27bd430a327c829ba23 Mon Sep 17 00:00:00 2001 From: cheynechen Date: Wed, 4 Sep 2024 16:06:23 +0800 Subject: [PATCH 1/2] =?UTF-8?q?minor:=20=E4=BB=A3=E7=A0=81=E8=B4=A8?= =?UTF-8?q?=E9=87=8F=E4=BC=98=E5=8C=96=20--story=3D119534560?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iam/api/http.py | 5 +- iam/contrib/django/dispatcher/dispatchers.py | 76 ++- .../management/commands/iam_makemigrations.py | 23 +- iam/contrib/tastypie/authorization.py | 108 ++++- itsm/helper/tasks.py | 166 ++++--- itsm/iadmin/contants.py | 5 +- itsm/postman/rpc/components/ticket_status.py | 32 +- itsm/ticket/models/ticket.py | 14 +- itsm/ticket/serializers/ticket.py | 14 +- itsm/ticket/tasks.py | 8 +- itsm/ticket/utils.py | 1 - itsm/ticket/views/operational.py | 451 ++++++++++++------ itsm/workflow/apps.py | 6 +- sops_proxy/urls.py | 4 +- 14 files changed, 639 insertions(+), 274 deletions(-) diff --git a/iam/api/http.py b/iam/api/http.py index 3007b3e6a..7006c35f6 100644 --- a/iam/api/http.py +++ b/iam/api/http.py @@ -106,7 +106,10 @@ def _http_request( if not logger.isEnabledFor(logging.DEBUG) and len(content) > 200: content = content[:200] + b"......" - message_format = "request: [method=`%s`, url=`%s`, data=`%s`] response: [status_code=`%s`, request_id=`%s`, content=`%s`]" # noqa + message_format = ( + "request: [method=`%s`, url=`%s`, data=`%s`] " + "response: [status_code=`%s`, request_id=`%s`, content=`%s`]" + ) # noqa if resp.status_code != 200: logger.error( message_format diff --git a/iam/contrib/django/dispatcher/dispatchers.py b/iam/contrib/django/dispatcher/dispatchers.py index b8e7d1a39..706f791a4 100644 --- a/iam/contrib/django/dispatcher/dispatchers.py +++ b/iam/contrib/django/dispatcher/dispatchers.py @@ -27,13 +27,18 @@ from iam.resource.utils import get_page_obj, get_filter_obj from iam.exceptions import AuthInvalidOperation -from iam.contrib.django.dispatcher.exceptions import InvalidPageException, KeywordTooShortException +from iam.contrib.django.dispatcher.exceptions import ( + InvalidPageException, + KeywordTooShortException, +) logger = logging.getLogger("iam") def fail_response(code, message, request_id): - response = JsonResponse({"code": code, "result": False, "message": message, "data": None}) + response = JsonResponse( + {"code": code, "result": False, "message": message, "data": None} + ) response["X-Request-Id"] = request_id return response @@ -52,14 +57,21 @@ def __init__(self, iam, system): def register(self, provider_type, provider): if not issubclass(type(provider), ResourceProvider): - raise AuthInvalidOperation("provider must be subclass of iam.resource.provider.ResourceProvider") + raise AuthInvalidOperation( + "provider must be subclass of iam.resource.provider.ResourceProvider" + ) if provider_type in self._provider: - raise AuthInvalidOperation("provider {} already been registered".format(provider_type)) + raise AuthInvalidOperation( + "provider {} already been registered".format(provider_type) + ) self._provider[provider_type] = provider - def as_view(self, decorators=[]): + def as_view(self, decorators=None): + if decorators is None: + decorators = [] + @csrf_exempt def view(request): return self._dispatch(request) @@ -78,45 +90,69 @@ def _dispatch(self, request): auth_allowed = self.iam.is_basic_auth_allowed(self.system, auth) if not auth_allowed: - logger.error("resource request({}) auth failed with auth param: {}".format(request_id, auth)) + logger.error( + "resource request({}) auth failed with auth param: {}".format( + request_id, auth + ) + ) return fail_response(401, "basic auth failed", request_id) # load json data try: data = json.loads(request.body) except Exception: - logger.error("resource request({}) failed with invalid body: {}".format(request_id, request.body)) + logger.error( + "resource request({}) failed with invalid body: {}".format( + request_id, request.body + ) + ) return fail_response(400, "reqeust body is not a valid json", request_id) # check basic params method = data.get("method") resource_type = data.get("type") if not (method and resource_type): - logger.error("resource request({}) failed with invalid data: {}".format(request_id, data)) + logger.error( + "resource request({}) failed with invalid data: {}".format( + request_id, data + ) + ) return fail_response(400, "method and type is required field", request_id) # check resource type if resource_type not in self._provider: logger.error( - "resource request({}) failed with unsupport resource type: {}".format(request_id, resource_type) + "resource request({}) failed with unsupport resource type: {}".format( + request_id, resource_type + ) + ) + return fail_response( + 404, "unsupport resource type: {}".format(resource_type), request_id ) - return fail_response(404, "unsupport resource type: {}".format(resource_type), request_id) # check method and process processor = getattr(self, "_dispatch_{}".format(method), None) if not processor: - logger.error("resource request({}) failed with unsupport method: {}".format(request_id, method)) + logger.error( + "resource request({}) failed with unsupport method: {}".format( + request_id, method + ) + ) return fail_response(404, "unsupport method: {}".format(method), request_id) logger.info( - "resource request({}) with filter: {}, page: {}".format(request_id, data.get("filter"), data.get("page")) + "resource request({}) with filter: {}, page: {}".format( + request_id, data.get("filter"), data.get("page") + ) ) try: return processor(request, data, request_id) except InvalidPageException as e: return fail_response(422, str(e), request_id) except Exception as e: - logger.exception("resource request({}) failed with exception: {}".format(request_id, e)) + logger.exception( + "resource request({}) failed with exception: {}".format(request_id, e) + ) return fail_response(500, str(e), request_id) def _get_options(self, request): @@ -154,7 +190,9 @@ def _dispatch_list_attr_value(self, request, data, request_id): def _dispatch_list_instance(self, request, data, request_id): options = self._get_options(request) - filter_obj = get_filter_obj(data.get("filter"), ["parent", "search", "resource_type_chain"]) + filter_obj = get_filter_obj( + data.get("filter"), ["parent", "search", "resource_type_chain"] + ) page_obj = get_page_obj(data.get("page")) provider = self._provider[data["type"]] @@ -204,7 +242,9 @@ def _dispatch_search_instance(self, request, data, request_id): filter_obj = get_filter_obj(data.get("filter"), ["parent", "keyword"]) if filter_obj.keyword is None or len(filter_obj.keyword) < 2: - raise KeywordTooShortException("the length of keyword should be greater than or equals to 2") + raise KeywordTooShortException( + "the length of keyword should be greater than or equals to 2" + ) page_obj = get_page_obj(data.get("page")) @@ -216,7 +256,11 @@ def _dispatch_search_instance(self, request, data, request_id): search_function = getattr(provider, "search_instance", None) if not (search_function and callable(search_function)): - return fail_response(404, "resource type: {} not support search instance".format(data["type"]), request_id) + return fail_response( + 404, + "resource type: {} not support search instance".format(data["type"]), + request_id, + ) result = provider.search_instance(filter_obj, page_obj, **options) diff --git a/iam/contrib/iam_migration/management/commands/iam_makemigrations.py b/iam/contrib/iam_migration/management/commands/iam_makemigrations.py index 73194f63e..67eab2b7b 100644 --- a/iam/contrib/iam_migration/management/commands/iam_makemigrations.py +++ b/iam/contrib/iam_migration/management/commands/iam_makemigrations.py @@ -29,7 +29,10 @@ class Command(BaseCommand): - help = "Create new migration for specific iam migration json file e.g. python manage.py iam_makemigrations migration.json" + help = ( + "Create new migration for specific iam migration json file " + "e.g. python manage.py iam_makemigrations migration.json" + ) def add_arguments(self, parser): parser.add_argument("migration_json", nargs="?", type=str) @@ -40,7 +43,9 @@ def handle(self, *args, **options): sys.stderr.write("please provide a migration json file name\n") exit(1) - json_path = getattr(settings, "BK_IAM_MIGRATION_JSON_PATH", "support-files/iam/") + json_path = getattr( + settings, "BK_IAM_MIGRATION_JSON_PATH", "support-files/iam/" + ) file_path = os.path.join(settings.BASE_DIR, json_path, json_file) if not os.path.exists(file_path): @@ -62,7 +67,11 @@ def handle(self, *args, **options): migration_name = self.migration_name(last_migration_name) migration_file = "{}.py".format( - os.path.join(settings.BASE_DIR, "iam/contrib/iam_migration/migrations", migration_name,) + os.path.join( + settings.BASE_DIR, + "iam/contrib/iam_migration/migrations", + migration_name, + ) ) with codecs.open(migration_file, mode="w", encoding="utf-8") as fp: @@ -83,7 +92,9 @@ def migration_name(self, last_migration_name): time = datetime.now().strftime("%Y%m%d%H%M") if not system_id: - self.stderr.write("You must set BK_IAM_SYSTEM_ID in django settings before make migrations") + self.stderr.write( + "You must set BK_IAM_SYSTEM_ID in django settings before make migrations" + ) exit(1) if not last_migration_name: @@ -91,4 +102,6 @@ def migration_name(self, last_migration_name): code = "%04d" % (int(last_migration_name[:4]) + 1) - return "{code}_{system_id}_{time}".format(code=code, system_id=system_id, time=time) + return "{code}_{system_id}_{time}".format( + code=code, system_id=system_id, time=time + ) diff --git a/iam/contrib/tastypie/authorization.py b/iam/contrib/tastypie/authorization.py index 1b75813ab..16e749c0f 100644 --- a/iam/contrib/tastypie/authorization.py +++ b/iam/contrib/tastypie/authorization.py @@ -29,14 +29,24 @@ @six.add_metaclass(abc.ABCMeta) class IAMAuthorizationHelper(object): def __init__( - self, system, create_action, read_action, update_action, delete_action, filter_key_mapping={}, + self, + system, + create_action, + read_action, + update_action, + delete_action, + filter_key_mapping=None, ): self.system = system self.create_action = create_action self.read_action = read_action self.update_action = update_action self.delete_action = delete_action - self.filter_key_mapping = filter_key_mapping + + if filter_key_mapping is None: + self.filter_key_mapping = {} + else: + self.filter_key_mapping = filter_key_mapping @abc.abstractmethod def get_subject(self, bundle): @@ -89,13 +99,27 @@ def create_detail(self, object_list, bundle): action = Action(self.helper.create_action) resources = self.helper.get_create_detail_resources(bundle) - request = Request(system, subject, action, resources, self.helper.get_create_detail_environment(bundle),) + request = Request( + system, + subject, + action, + resources, + self.helper.get_create_detail_environment(bundle), + ) allowed = self.iam.is_allowed(request) - logger.debug("tastypie create_detail is_allowed request({}) result: {}".format(request.to_dict(), allowed)) + logger.debug( + "tastypie create_detail is_allowed request({}) result: {}".format( + request.to_dict(), allowed + ) + ) if not allowed: - raise ImmediateHttpResponse(IAMAuthFailedResponse(AuthFailedException(system, subject, action, resources))) + raise ImmediateHttpResponse( + IAMAuthFailedResponse( + AuthFailedException(system, subject, action, resources) + ) + ) return allowed @@ -110,13 +134,27 @@ def update_detail(self, object_list, bundle): action = Action(self.helper.update_action) resources = self.helper.get_update_detail_resources(bundle) - request = Request(system, subject, action, resources, self.helper.get_update_detail_environment(bundle),) + request = Request( + system, + subject, + action, + resources, + self.helper.get_update_detail_environment(bundle), + ) allowed = self.iam.is_allowed(request) - logger.debug("tastypie update_detail is_allowed request({}) result: {}".format(request.to_dict(), allowed)) + logger.debug( + "tastypie update_detail is_allowed request({}) result: {}".format( + request.to_dict(), allowed + ) + ) if not allowed: - raise ImmediateHttpResponse(IAMAuthFailedResponse(AuthFailedException(system, subject, action, resources))) + raise ImmediateHttpResponse( + IAMAuthFailedResponse( + AuthFailedException(system, subject, action, resources) + ) + ) return allowed @@ -131,13 +169,27 @@ def delete_detail(self, object_list, bundle): action = Action(self.helper.delete_action) resources = self.helper.get_delete_detail_resources(bundle) - request = Request(system, subject, action, resources, self.helper.get_delete_detail_environment(bundle),) + request = Request( + system, + subject, + action, + resources, + self.helper.get_delete_detail_environment(bundle), + ) allowed = self.iam.is_allowed(request) - logger.debug("tastypie delete_detail is_allowed request({}) result: {}".format(request.to_dict(), allowed)) + logger.debug( + "tastypie delete_detail is_allowed request({}) result: {}".format( + request.to_dict(), allowed + ) + ) if not allowed: - raise ImmediateHttpResponse(IAMAuthFailedResponse(AuthFailedException(system, subject, action, resources))) + raise ImmediateHttpResponse( + IAMAuthFailedResponse( + AuthFailedException(system, subject, action, resources) + ) + ) return allowed @@ -149,13 +201,27 @@ def read_detail(self, object_list, bundle): action = Action(self.helper.read_action) resources = self.helper.get_read_detail_resources(bundle) - request = Request(system, subject, action, resources, self.helper.get_read_detail_environment(bundle),) + request = Request( + system, + subject, + action, + resources, + self.helper.get_read_detail_environment(bundle), + ) allowed = self.iam.is_allowed(request) - logger.debug("tastypie read_detail is_allowed request({}) result: {}".format(request.to_dict(), allowed)) + logger.debug( + "tastypie read_detail is_allowed request({}) result: {}".format( + request.to_dict(), allowed + ) + ) if not allowed: - raise ImmediateHttpResponse(IAMAuthFailedResponse(AuthFailedException(system, subject, action, resources))) + raise ImmediateHttpResponse( + IAMAuthFailedResponse( + AuthFailedException(system, subject, action, resources) + ) + ) return allowed @@ -173,14 +239,18 @@ def __init__(self, iam, helper): class ReadOnlyCompleteListIAMAuthorization( - IAMReadDetailAuthorizationMixin, IAMReadOnlyAuthorization, + IAMReadDetailAuthorizationMixin, + IAMReadOnlyAuthorization, ): def read_list(self, object_list, bundle): return object_list class CustomCreateCompleteListIAMAuthorization( - IAMUpdateAuthorizationMixin, IAMDeleteAuthorizationMixin, IAMReadDetailAuthorizationMixin, IAMAuthorization, + IAMUpdateAuthorizationMixin, + IAMDeleteAuthorizationMixin, + IAMReadDetailAuthorizationMixin, + IAMAuthorization, ): def read_list(self, object_list, bundle): return object_list @@ -216,5 +286,9 @@ def read_list(self, object_list, bundle): environment=self.get_read_list_environment(bundle), ) f = self.iam.make_filter(request, key_mapping=self.helper.filter_key_mapping) - logger.debug("tastypie read_list make_filter request({}) result: {}".format(request.to_dict(), f)) + logger.debug( + "tastypie read_list make_filter request({}) result: {}".format( + request.to_dict(), f + ) + ) return object_list.filter(f) diff --git a/itsm/helper/tasks.py b/itsm/helper/tasks.py index 963e1a455..7e41c326c 100644 --- a/itsm/helper/tasks.py +++ b/itsm/helper/tasks.py @@ -24,7 +24,7 @@ """ # celery 任务示例 -# +# # 本地启动celery命令: python manage.py celery worker --settings=settings # 周期性任务还需要启动celery调度命令:python manage.py celerybeat --settings=settings @@ -76,25 +76,24 @@ def _db_fix_for_blueapps_after_2_6_0(): """ blueapps的数据升级 """ - migrations = ( - ('account', '0002_init_superuser'), - ('account', '0003_verifyinfo') - ) + migrations = (("account", "0002_init_superuser"), ("account", "0003_verifyinfo")) if settings.RUN_VER != "open": - logger.Exception("当前运行环境为:{},不支持db_fix_for_blueapps_after_2_6_0方法".format( - settings.RUN_VER)) + logger.Exception( + "当前运行环境为:{},不支持db_fix_for_blueapps_after_2_6_0方法".format(settings.RUN_VER) + ) return try: with connection.cursor() as cursor: - cursor.execute('SELECT `app`, `name` FROM django_migrations;') + cursor.execute("SELECT `app`, `name` FROM django_migrations;") rows = cursor.fetchall() for migration in migrations: if migration in rows: continue dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") cursor.execute( - "INSERT INTO `django_migrations` (`app`, `name`, `applied`) VALUES (\"{}\", \"{}\", \"{}\");".format( - migration[0], migration[1], dt)) + "INSERT INTO `django_migrations` (`app`, `name`, `applied`) " + 'VALUES ("{}", "{}", "{}");'.format(migration[0], migration[1], dt) + ) except BaseException as err: logger.Exception(str(err)) @@ -116,12 +115,16 @@ def create_task(instances): task_config = [ dict( task_schema_id=schema_id, - create_task_state=task_settings['create_task_state'], - execute_task_state=task_settings['execute_task_state'], - execute_can_create=task_settings.get('execute_can_create', False), - need_task_finished=task_settings.get('need_task_finished', False), + create_task_state=task_settings["create_task_state"], + execute_task_state=task_settings["execute_task_state"], + execute_can_create=task_settings.get( + "execute_can_create", False + ), + need_task_finished=task_settings.get( + "need_task_finished", False + ), ) - for schema_id in task_settings.get('task_schema_ids', []) + for schema_id in task_settings.get("task_schema_ids", []) ] workflow.create_task(task_config) except BaseException as err: @@ -135,12 +138,12 @@ def create_task(instances): @task def _db_fix_for_service_catalog(): """服务目录添加前置路径""" - print('start execute _db_fix_for_service_catalog') + print("start execute _db_fix_for_service_catalog") for s_c in ServiceCatalog.objects.all(): s_c.route = list(s_c.get_ancestors(include_self=True).values("id", "name")) s_c.save(update_fields=("route",)) - print('fix: s_c: %s' % s_c.id) - print('finish execute _db_fix_for_service_catalog') + print("fix: s_c: %s" % s_c.id) + print("finish execute _db_fix_for_service_catalog") @task @@ -161,16 +164,22 @@ def update_workflow_fields(): fields = Field._objects.filter(type="FILE").select_related("workflow", "state") for field in fields: base_file_path = os.path.join( - "workflow_{workflow_id}_{state_id}".format(workflow_id=field.workflow_id, state_id=field.state_id), + "workflow_{workflow_id}_{state_id}".format( + workflow_id=field.workflow_id, state_id=field.state_id + ), field.key, ) new_choice = {} if isinstance(field.choice, dict): continue for file in field.choice: - unique_key = hashlib.md5("{}{}".format(datetime.datetime.now(), file["name"]).encode()) + unique_key = hashlib.md5( + "{}{}".format(datetime.datetime.now(), file["name"]).encode() + ) file_path = os.path.join(base_file_path, file["name"]) - new_choice[unique_key.hexdigest()] = dict(name=file["name"], path=file_path) + new_choice[unique_key.hexdigest()] = dict( + name=file["name"], path=file_path + ) field.choice = new_choice bulk_update(fields, update_fields=["choice"]) @@ -190,32 +199,47 @@ def update_version_fields(): if isinstance(field["choice"], dict): continue for file in field["choice"]: - unique_key = hashlib.md5("{}{}".format(datetime.datetime.now(), file["name"]).encode()) + unique_key = hashlib.md5( + "{}{}".format(datetime.datetime.now(), file["name"]).encode() + ) file_path = os.path.join(base_file_path, file["name"]) - new_choice[unique_key.hexdigest()] = dict(name=file["name"], path=file_path) + new_choice[unique_key.hexdigest()] = dict( + name=file["name"], path=file_path + ) field["choice"] = new_choice bulk_update(flows, update_fields=["fields"]) def update_ticket_fields(): - ticket_fields = TicketField._objects.filter(type="FILE").select_related("ticket") + ticket_fields = TicketField._objects.filter(type="FILE").select_related( + "ticket" + ) for field in ticket_fields: if field.choice: - for item in WorkflowVersion._objects.get(id=field.ticket.flow_id).fields.values(): + for item in WorkflowVersion._objects.get( + id=field.ticket.flow_id + ).fields.values(): if item["key"] == field.key: field.choice = item["choice"] break field_value = {} base_file_path = os.path.join( - "{ticket_id}_{state_id}".format(ticket_id=field.ticket_id, state_id=field.state_id), field.key, + "{ticket_id}_{state_id}".format( + ticket_id=field.ticket_id, state_id=field.state_id + ), + field.key, ) for ticket_file in field.value.split(","): if not ticket_file: continue - unique_key = hashlib.md5("{}{}".format(datetime.datetime.now(), ticket_file).encode()) + unique_key = hashlib.md5( + "{}{}".format(datetime.datetime.now(), ticket_file).encode() + ) file_path = os.path.join(base_file_path, ticket_file) - field_value[unique_key.hexdigest()] = dict(path=file_path, name=ticket_file) + field_value[unique_key.hexdigest()] = dict( + path=file_path, name=ticket_file + ) field._value = json.dumps(field_value) bulk_update(ticket_fields, update_fields=["_value", "choice"]) @@ -235,7 +259,9 @@ def _db_fix_from_2_1_x_to_2_2_1(): return task_start = datetime.datetime.now() SystemSettings.objects.create( - key="_db_fix_from_2_1_x_to_2_2_1", value="start: %s" % task_start, type="DATETIME", + key="_db_fix_from_2_1_x_to_2_2_1", + value="start: %s" % task_start, + type="DATETIME", ) kwargs = build_field_kwargs() @@ -247,26 +273,41 @@ def _db_fix_from_2_1_x_to_2_2_1(): Service.objects.upgrade_services_flow(**kwargs) Workflow.objects.upgrade_workflow(for_migrate=True, **kwargs) - bad_states = [state.id for state in State.objects.all() if state.followers_type in ["", None, "OPEN"]] + bad_states = [ + state.id + for state in State.objects.all() + if state.followers_type in ["", None, "OPEN"] + ] State.objects.filter(pk__in=bad_states).update(followers_type="EMPTY") Workflow._objects.filter(flow_type=DEFAULT_STRING).update(flow_type=F("service")) DefaultField.objects.update(related_fields={}) - end_tickets = Ticket.objects.filter(current_status__in=["FINISHED", "TERMINATED"]).values_list("id", flat=True) - TicketField.objects.filter(ticket_id__in=end_tickets, source_type="API").update(source_type="CUSTOM") + end_tickets = Ticket.objects.filter( + current_status__in=["FINISHED", "TERMINATED"] + ).values_list("id", flat=True) + TicketField.objects.filter(ticket_id__in=end_tickets, source_type="API").update( + source_type="CUSTOM" + ) - end_tickets = Ticket.objects.filter(current_status__in=["FINISHED", "TERMINATED"]).values_list("id", flat=True) - TicketField.objects.filter(ticket_id__in=end_tickets, source_type="API").update(source_type="CUSTOM") + end_tickets = Ticket.objects.filter( + current_status__in=["FINISHED", "TERMINATED"] + ).values_list("id", flat=True) + TicketField.objects.filter(ticket_id__in=end_tickets, source_type="API").update( + source_type="CUSTOM" + ) TicketEventLog.objects.filter(message__in=["流程开始", "单据流程结束"]).update(source="SYS") task_end = datetime.datetime.now() SystemSettings.objects.filter(key="_db_fix_from_2_1_x_to_2_2_1").update( - value="start: %s, end: %s, use: %s" % (task_start, task_end, (task_end - task_start)) + value="start: %s, end: %s, use: %s" + % (task_start, task_end, (task_end - task_start)) + ) + logger.info( + "-------------------db_fix_from_2_1_x_to_2_2_1: finished ------------------------\n" ) - logger.info("-------------------db_fix_from_2_1_x_to_2_2_1: finished ------------------------\n") @task @@ -278,16 +319,18 @@ def _db_fix_from_1_1_22_to_2_1_x(): return SystemSettings.objects.create( - key="_db_fix_from_1_1_22_to_2_1_16", value=datetime.datetime.now(), type="DATETIME", + key="_db_fix_from_1_1_22_to_2_1_16", + value=datetime.datetime.now(), + type="DATETIME", ) logger.info("_db_fix_from_1_1_22_to_2_1_x start") try: _fix_ticket_title() _update_logs_type() _db_fix_after_2_0_3() - Ticket.objects.filter(is_draft=False, current_status="FINISHED", end_at__isnull=True).update( - end_at=F("update_at") - ) + Ticket.objects.filter( + is_draft=False, current_status="FINISHED", end_at__isnull=True + ).update(end_at=F("update_at")) for log in TicketEventLog.objects.filter(type="CLAIM", deal_time=0): log.update_deal_time() _db_fix_after_2_0_7() @@ -325,7 +368,9 @@ def _db_fix_after_2_0_3(): "fix_workflow_snapshot={}\n" "fix_ticket_eventlog={}\n" "fix_invite_comment={}\n" - "fix_followers={}\n".format(version, cnt0, cnt1, cnt2, cnt3, cnt4, cnt5, cnt6) + "fix_followers={}\n".format( + version, cnt0, cnt1, cnt2, cnt3, cnt4, cnt5, cnt6 + ) ) logger.info("db_fix_after_2_0_3 success!") except Exception as e: @@ -350,10 +395,14 @@ def _db_fix_after_2_0_7(): fix_end_logs() end = datetime.datetime.now() - print(("_db_fix_after_2_0_7 end-start: %s - %s = %s" % (end, start, end - start))) + print( + ("_db_fix_after_2_0_7 end-start: %s - %s = %s" % (end, start, end - start)) + ) logger.info("db_fix_after_2_0_7 success!") except Exception as e: - logger.error("db_fix_after_2_0_7 fail! version: %s, error: %s" % (version, str(e))) + logger.error( + "db_fix_after_2_0_7 fail! version: %s, error: %s" % (version, str(e)) + ) @task @@ -361,7 +410,8 @@ def _db_fix_after_2_0_9(): try: cnt = 0 for ticket in Ticket.objects.filter( - current_status__in=["DISTRIBUTING", "DISTRIBUTING-RECEIVING"], current_assignor_type="PERSON", + current_status__in=["DISTRIBUTING", "DISTRIBUTING-RECEIVING"], + current_assignor_type="PERSON", ): flag = 0 if not ticket.current_assignor.startswith(","): @@ -426,9 +476,9 @@ def _db_fix_after_2_1_x(): @task def _db_fix_after_2_0_14(): try: - Ticket.objects.filter(current_status="FINISHED", current_processors_type="OPEN").update( - current_processors_type="" - ) + Ticket.objects.filter( + current_status="FINISHED", current_processors_type="OPEN" + ).update(current_processors_type="") logger.info("db_fix_after_2_0_14 success!") except Exception as e: logger.error("db_fix_after_2_0_14 fail! error: %s" % str(e)) @@ -437,7 +487,9 @@ def _db_fix_after_2_0_14(): @task def _db_fix_after_2_1_1(): try: - TicketEventLog.objects.filter(message__contains="驳回").update(type=REJECT_OPERATE) + TicketEventLog.objects.filter(message__contains="驳回").update( + type=REJECT_OPERATE + ) logger.info("db_fix_after_2_1_1 success!") except Exception as e: logger.error("db_fix_after_2_1_1 fail! error: %s" % str(e)) @@ -460,7 +512,9 @@ def _fix_ticket_title(): @task def _update_logs_type(): try: - TicketEventLog.objects.filter(message__contains="】终止,原因:【").update(type="TERMINATE", is_valid=True) + TicketEventLog.objects.filter(message__contains="】终止,原因:【").update( + type="TERMINATE", is_valid=True + ) logger.info("update_logs_type success!") except Exception as e: logger.error("update_logs_type fail! error: %s" % str(e)) @@ -469,8 +523,12 @@ def _update_logs_type(): @task def _db_fix_sla(): try: - choices = OldSla.objects.values("name", "level", "resp_time", "deal_time", "id", "desc", "key", "is_builtin") - need_fix_query = TicketField.objects.filter(key="fault_level", create_at__gte="2019-05-25 00:00:00") + choices = OldSla.objects.values( + "name", "level", "resp_time", "deal_time", "id", "desc", "key", "is_builtin" + ) + need_fix_query = TicketField.objects.filter( + key="fault_level", create_at__gte="2019-05-25 00:00:00" + ) need_fix_query.update(choice=choices) choices_dict = {str(choice["id"]): choice["key"] for choice in choices} for field in need_fix_query: @@ -496,9 +554,9 @@ def _db_fix_after_2_1_9(): @task def _db_fix_ticket_end_at_after_2_0_5(): try: - Ticket.objects.filter(is_draft=False, current_status="FINISHED", end_at__isnull=True).update( - end_at=F("update_at") - ) + Ticket.objects.filter( + is_draft=False, current_status="FINISHED", end_at__isnull=True + ).update(end_at=F("update_at")) logger.info("_db_fix_ticket_end_at_after_2_0_5 success") except Exception as e: logger.error("_db_fix_ticket_end_at_after_2_0_5 fail!, error: %s" % str(e)) diff --git a/itsm/iadmin/contants.py b/itsm/iadmin/contants.py index 6bc2340e0..0f7994861 100644 --- a/itsm/iadmin/contants.py +++ b/itsm/iadmin/contants.py @@ -407,7 +407,10 @@ 单号:${sn} ${message}""" -GENERAL_CONTENT_DONE = SMS_CONTENT_DONE = WEIXIN_CONTENT_DONE = """您的需求(${title})已经处理完成,现邀请您为我们的服务进行评价。您的反馈对我们非常重要!感谢回复与建议,祝您工作愉快! +GENERAL_CONTENT_DONE = SMS_CONTENT_DONE = WEIXIN_CONTENT_DONE = """您的需求(${title})已经处理完成, +现邀请您为我们的服务进行评价。 +您的反馈对我们非常重要! +感谢回复与建议,祝您工作愉快! ${ticket_url}""" # noqa GENERAL_CONTENT_FOLLOW = SMS_CONTENT_FOLLOW = WEIXIN_CONTENT_FOLLOW = """你有一条${service_type_name}工单需要关注 diff --git a/itsm/postman/rpc/components/ticket_status.py b/itsm/postman/rpc/components/ticket_status.py index 3e6dec820..118e1ba16 100644 --- a/itsm/postman/rpc/components/ticket_status.py +++ b/itsm/postman/rpc/components/ticket_status.py @@ -47,22 +47,29 @@ def clean(self): ticket_id = self.data.get("ticket_id") if ticket_id: ticket = Ticket.objects.get(id=ticket_id) - cleaned_data.update(current_status=ticket.current_status, - service_type=ticket.service_type) + cleaned_data.update( + current_status=ticket.current_status, + service_type=ticket.service_type, + ) return cleaned_data def handle(self): if "service_type" in self.form_data: ticket_status = TicketStatus.objects.get( - key=self.form_data["current_status"], service_type=self.form_data["service_type"] + key=self.form_data["current_status"], + service_type=self.form_data["service_type"], ) - payload = TicketStatusOptionSerializer(ticket_status.to_status, many=True).data + payload = TicketStatusOptionSerializer( + ticket_status.to_status, many=True + ).data else: payload = [] ticket_status_dict = OrderedDict() # 获取所有非结束的工单状态 - ticket_statuses = TicketStatus.objects.filter(is_over=False).order_by("order") + ticket_statuses = TicketStatus.objects.filter(is_over=False).order_by( + "order" + ) # 将相同key的单据状态合并 for ticket_status in ticket_statuses: @@ -76,7 +83,9 @@ def handle(self): "other_info_list": [other_info], } else: - ticket_status_dict[ticket_status.key]["other_info_list"].append(other_info) + ticket_status_dict[ticket_status.key]["other_info_list"].append( + other_info + ) for key, info in ticket_status_dict.items(): name_dict = defaultdict(set) @@ -88,7 +97,10 @@ def handle(self): for name, service_types in name_dict.items() ] payload.append( - {"key": key, "name": ",".join(name_list), } + { + "key": key, + "name": ",".join(name_list), + } ) self.response.payload = payload @@ -96,7 +108,9 @@ def handle(self): @staticmethod def get_service_type_display_name(service_types): """友好显示服务类型""" - if len(set(SERVICE_CATEGORY.keys()).difference(service_types)): - return ",".join(_(SERVICE_CATEGORY[service_type]) for service_type in service_types) + if len(set(SERVICE_CATEGORY.keys()).difference(service_types)) > 0: + return ",".join( + _(SERVICE_CATEGORY[service_type]) for service_type in service_types + ) else: return _("所有服务类型") diff --git a/itsm/ticket/models/ticket.py b/itsm/ticket/models/ticket.py index 9ee96e49b..00364772d 100644 --- a/itsm/ticket/models/ticket.py +++ b/itsm/ticket/models/ticket.py @@ -143,7 +143,7 @@ BK_PLUGIN_STATE, SUSPENDED, SHOW_BY_CONDITION, - VARIABLE_LEADER, XSS_FIELD_TYPE, + VARIABLE_LEADER, ) from itsm.component.constants.trigger import ( CREATE_TICKET, @@ -3082,7 +3082,7 @@ def fill_state_fields(self, fields): filter_field_query_set = self.fields.filter(key__in=fields_map.keys()) for ticket_field in filter_field_query_set: ticket_field.value = fields_map[ticket_field.key]["value"] - # 针对字符串类型进行 xss 过滤 + # 针对字符串类型进行 xss 过滤 if isinstance(ticket_field.value, str): ticket_field.value = texteditor_escape(ticket_field.value) @@ -3783,8 +3783,8 @@ def _formatted(processors_type, processors): node_processors = [] task_processors = [] for node_status in self.node_status.filter( - Q(status__in=Status.CAN_OPERATE_STATUS) | - Q(status=FAILED, type__in=[TASK_STATE, TASK_SOPS_STATE]) + Q(status__in=Status.CAN_OPERATE_STATUS) + | Q(status=FAILED, type__in=[TASK_STATE, TASK_SOPS_STATE]) ): # Get all types node processors if node_status.type in [SIGN_STATE, APPROVAL_STATE]: @@ -4063,13 +4063,11 @@ def display_content(field_type, content): label = data.get("label") if label and "【" not in label: label = "【{}】".format(label) - + detail = [] if isinstance(data["value"], str): if label and data["value"]: - text_content.append( - "{}\n{}".format(label, data["value"]) - ) + text_content.append("{}\n{}".format(label, data["value"])) continue for attr in data["value"]: single = [] diff --git a/itsm/ticket/serializers/ticket.py b/itsm/ticket/serializers/ticket.py index 15a497296..980b91b16 100644 --- a/itsm/ticket/serializers/ticket.py +++ b/itsm/ticket/serializers/ticket.py @@ -24,8 +24,6 @@ """ import copy -import json - from collections import OrderedDict from datetime import datetime @@ -35,9 +33,8 @@ from rest_framework.fields import JSONField, empty from common.log import logger +from common.utils import html_escape from itsm.auth_iam.utils import IamRequest -from itsm.component.drf.serializers import AuthModelSerializer -from itsm.component.utils.client_backend_query import get_bk_users from itsm.component.constants import ( ACTION_CHOICES, ALL_ACTION_CHOICES, @@ -76,6 +73,7 @@ SUSPENDED, ) from itsm.component.dlls.component import ComponentLibrary +from itsm.component.drf.serializers import AuthModelSerializer from itsm.component.exceptions import TriggerValidateError from itsm.component.utils.basic import ( better_time_or_none, @@ -83,12 +81,12 @@ generate_random_sn, ) from itsm.component.utils.client_backend_query import get_biz_names, get_template_list +from itsm.component.utils.client_backend_query import get_bk_users from itsm.component.utils.misc import ( transform_single_username, transform_username, get_transform_username_dict, ) -from common.utils import html_escape from itsm.postman.serializers import TaskStateApiInfoSerializer from itsm.service.validators import service_validate from itsm.sla_engine.constants import HANDLE_TIMEOUT, RUNNING as SLA_RUNNING, PAUSED @@ -109,17 +107,17 @@ SlaTicketHighlight, TicketRemark, ) -from itsm.ticket.tasks import remark_notify -from itsm.ticket.utils import compute_list_difference, get_user_profile -from itsm.workflow.models import WorkflowVersion from itsm.ticket.serializers.field import ( FieldSerializer, FieldSimpleSerializer, TableFieldSerializer, TaskFieldSerializer, ) +from itsm.ticket.tasks import remark_notify +from itsm.ticket.utils import compute_list_difference, get_user_profile from itsm.ticket.validators import CreateTicketValidator, StateOperateValidator from itsm.ticket_status.models import TicketStatus +from itsm.workflow.models import WorkflowVersion BkUser = get_user_model() diff --git a/itsm/ticket/tasks.py b/itsm/ticket/tasks.py index f7d44f7ad..d5fd9e1d0 100644 --- a/itsm/ticket/tasks.py +++ b/itsm/ticket/tasks.py @@ -166,7 +166,13 @@ def weekly_statical(): new_services = [service.name for service in namedtuplefetchall(cursor)] # 增加的业务信息 - sql_bizs_last_week_ago = "select bk_biz_id from ticket_ticket where create_at > '%s' and is_deleted = false group by bk_biz_id " # noqa + sql_bizs_last_week_ago = ( + "select bk_biz_id " + "from ticket_ticket " + "where create_at > '%s' " + "and is_deleted = false " + "group by bk_biz_id " + ) # noqa new_bizs = [] # 新增的业务 with connection.cursor() as cursor: cursor.execute(sql_bizs_last_week_ago) diff --git a/itsm/ticket/utils.py b/itsm/ticket/utils.py index 779512ae4..bd15c1a70 100644 --- a/itsm/ticket/utils.py +++ b/itsm/ticket/utils.py @@ -224,4 +224,3 @@ def get_user_profile(username): except Exception: profile = {"name": "", "phone": "", "departments": []} return profile - diff --git a/itsm/ticket/views/operational.py b/itsm/ticket/views/operational.py index 2cdeefd8b..41b658ff5 100644 --- a/itsm/ticket/views/operational.py +++ b/itsm/ticket/views/operational.py @@ -35,8 +35,13 @@ from rest_framework.response import Response from dateutil.relativedelta import relativedelta -from itsm.component.constants import CLAIM_OPERATE, DISTRIBUTE_OPERATE, SERVICE_LIST, \ - TRANSITION_OPERATE, SYSTEM_OPERATE +from itsm.component.constants import ( + CLAIM_OPERATE, + DISTRIBUTE_OPERATE, + SERVICE_LIST, + TRANSITION_OPERATE, + SYSTEM_OPERATE, +) from itsm.component.drf import viewsets as component_viewsets from itsm.component.drf.pagination import CustomPageNumberPagination from itsm.component.exceptions import ParamError @@ -49,8 +54,14 @@ transform_single_username, ) from itsm.component.utils.user_count import user_count, get_user_statistics -from itsm.ticket.models import Ticket, TicketComment, TicketEventLog, TicketField, TicketStatus, \ - TicketOrganization +from itsm.ticket.models import ( + Ticket, + TicketComment, + TicketEventLog, + TicketField, + TicketStatus, + TicketOrganization, +) from itsm.ticket.serializers import ( CommentSerializer, OperationalDataTicketSerializer, @@ -85,7 +96,7 @@ class OperationalDataViewSet(component_viewsets.ReadOnlyModelViewSet): filter_fields = { "create_at": ["lte", "gte", "lt", "gt"], } - ordering_fields = ('create_at', 'priority_order', 'current_status_order') + ordering_fields = ("create_at", "priority_order", "current_status_order") @action(detail=False, methods=["get"]) def overview_count(self, request, *args, **kwargs): @@ -96,9 +107,13 @@ def overview_count(self, request, *args, **kwargs): return Response( { "count": project_analysis.get_ticket_count(), - "service_count": 1 if service_id else project_analysis.get_service_count(), + "service_count": 1 + if service_id + else project_analysis.get_service_count(), "biz_count": project_analysis.get_biz_count(), - "user_count": project_analysis.get_ticket_user_count() if service_id else user_count(project_key=project_key), + "user_count": project_analysis.get_ticket_user_count() + if service_id + else user_count(project_key=project_key), } ) @@ -106,8 +121,12 @@ def overview_count(self, request, *args, **kwargs): def compared_same_week(self, request): now = datetime.now() # 今天零点 - zero_now = now - timedelta(hours=now.hour, minutes=now.minute, seconds=now.second, - microseconds=now.microsecond) + zero_now = now - timedelta( + hours=now.hour, + minutes=now.minute, + seconds=now.second, + microseconds=now.microsecond, + ) # 本周第一天和今天 this_week_start = zero_now - timedelta(days=zero_now.weekday()) this_week_end = now @@ -121,37 +140,60 @@ def compared_same_week(self, request): this_scope = (this_week_start, this_week_end) last_scope = (last_week_start, last_week_end) - this_week_project_analysis = ProjectOperationalData(service_id, this_scope, project_key) - last_week_project_analysis = ProjectOperationalData(service_id, last_scope, project_key) + this_week_project_analysis = ProjectOperationalData( + service_id, this_scope, project_key + ) + last_week_project_analysis = ProjectOperationalData( + service_id, last_scope, project_key + ) this_week_ticket_count = this_week_project_analysis.get_ticket_count() last_week_ticket_count = last_week_project_analysis.get_ticket_count() ticket_ratio = round( - (this_week_ticket_count - last_week_ticket_count) / (last_week_ticket_count or 1) * 100, - 2) + (this_week_ticket_count - last_week_ticket_count) + / (last_week_ticket_count or 1) + * 100, + 2, + ) - this_week_service_count = 1 if service_id else this_week_project_analysis.get_service_count() - last_week_service_count = 1 if service_id else last_week_project_analysis.get_service_count() + this_week_service_count = ( + 1 if service_id else this_week_project_analysis.get_service_count() + ) + last_week_service_count = ( + 1 if service_id else last_week_project_analysis.get_service_count() + ) service_ratio = round( - (this_week_service_count - last_week_service_count) / ( - last_week_service_count or 1) * 100, 2 + (this_week_service_count - last_week_service_count) + / (last_week_service_count or 1) + * 100, + 2, ) this_week_biz_count = this_week_project_analysis.get_biz_count() last_week_biz_count = last_week_project_analysis.get_biz_count() biz_ratio = round( - (this_week_biz_count - last_week_biz_count) / (last_week_biz_count or 1) * 100, 2) + (this_week_biz_count - last_week_biz_count) + / (last_week_biz_count or 1) + * 100, + 2, + ) this_week_user_count = ( - this_week_project_analysis.get_ticket_user_count() if service_id else user_count( - this_week=this_scope, project_key=project_key) + this_week_project_analysis.get_ticket_user_count() + if service_id + else user_count(this_week=this_scope, project_key=project_key) ) last_week_user_count = ( - last_week_project_analysis.get_ticket_user_count() if service_id else user_count( - last_week=last_scope, project_key=project_key) + last_week_project_analysis.get_ticket_user_count() + if service_id + else user_count(last_week=last_scope, project_key=project_key) ) user_ratio = round( - (this_week_user_count - last_week_user_count) / (last_week_user_count or 1) * 100, 2) + (this_week_user_count - last_week_user_count) + / (last_week_user_count or 1) + * 100, + 2, + ) return Response( { @@ -198,24 +240,30 @@ def service_statistics(self, request): if service_name: services = services.filter(name__icontains=service_name) services = services.values("id", "name", "key") - service_dict = {service["id"]: {"name": service["name"], "key": service["key"]} for service - in services} + service_dict = { + service["id"]: {"name": service["name"], "key": service["key"]} + for service in services + } service_category = ServiceCategory.objects.all().values("key", "name") - category_dict = {category["key"]: category["name"] for category in service_category} + category_dict = { + category["key"]: category["name"] for category in service_category + } order = kwargs.pop("order_by") ticket_info = ( self.queryset.filter(**kwargs) - .filter(service_id__in=service_dict.keys()) - .values("service_id") - .annotate( + .filter(service_id__in=service_dict.keys()) + .values("service_id") + .annotate( count=Count("id"), creator_count=Count("creator", distinct=True), - biz_count=Count(Case(When(bk_biz_id__gt=-1, then='bk_biz_id')), distinct=True), + biz_count=Count( + Case(When(bk_biz_id__gt=-1, then="bk_biz_id")), distinct=True + ), ) - .order_by(order) + .order_by(order) ) service_info = [] @@ -226,11 +274,15 @@ def service_statistics(self, request): { "service_id": ticket["service_id"], "service_name": service_dict[ticket["service_id"]]["name"], - "category": category_dict[service_dict[ticket["service_id"]]["key"]], + "category": category_dict[ + service_dict[ticket["service_id"]]["key"] + ], "count": ticket["count"], "creator_count": ticket["creator_count"], "biz_count": ticket["biz_count"], - "ratio": "{}%".format(round(ticket["count"] / ticket_count * 100, 2)), + "ratio": "{}%".format( + round(ticket["count"] / ticket_count * 100, 2) + ), } ) return self.get_paginated_response(service_info) @@ -253,8 +305,10 @@ def biz_statistics(self, request): ticket_info = ( queryset.values("bk_biz_id") - .annotate(count=Count("id"), service_count=Count("service_id", distinct=True)) - .order_by(order) + .annotate( + count=Count("id"), service_count=Count("service_id", distinct=True) + ) + .order_by(order) ) ticket_info = self.paginate_queryset(ticket_info) biz_info = [] @@ -277,15 +331,24 @@ def category_statistics(self, request): kwargs = filter_serializer.validated_data order = kwargs.pop("order_by") service_category = ServiceCategory.objects.all().values("key", "name") - category_dict = {category["key"]: category["name"] for category in service_category} + category_dict = { + category["key"]: category["name"] for category in service_category + } - ticket_info = self.queryset.filter(**kwargs).values("service_type").annotate( - count=Count("id")).order_by(order) + ticket_info = ( + self.queryset.filter(**kwargs) + .values("service_type") + .annotate(count=Count("id")) + .order_by(order) + ) if project_key: ticket_info = ticket_info.filter(project_key=project_key) category_info = [ - {"service_type": category_dict[ticket["service_type"]], "count": ticket["count"]} for - ticket in ticket_info + { + "service_type": category_dict[ticket["service_type"]], + "count": ticket["count"], + } + for ticket in ticket_info ] return Response(category_info) @@ -298,19 +361,26 @@ def status_statistics(self, request): kwargs = filter_serializer.validated_data not_running_status = ["FINISHED", "TERMINATED", "REVOKED"] ticket_info = ( - self.queryset.filter(**kwargs).values("current_status").annotate( - count=Count("id")).order_by("-count") + self.queryset.filter(**kwargs) + .values("current_status") + .annotate(count=Count("id")) + .order_by("-count") ) if project_key: ticket_info = ticket_info.filter(project_key=project_key) category_info = {} for ticket in ticket_info: if ticket["current_status"] in not_running_status: - category_info[ticket["current_status"]] = {"status": ticket["current_status"], - "count": ticket["count"]} + category_info[ticket["current_status"]] = { + "status": ticket["current_status"], + "count": ticket["count"], + } continue if "RUNNING" not in category_info: - category_info["RUNNING"] = {"status": "RUNNING", "count": ticket["count"]} + category_info["RUNNING"] = { + "status": "RUNNING", + "count": ticket["count"], + } else: category_info["RUNNING"]["count"] += ticket["count"] @@ -365,29 +435,36 @@ def top_creator_statistics(self, request): kwargs.pop("timedelta") max_ids = ( TicketOrganization.objects.filter( - create_at__gte=kwargs["create_at__gte"], create_at__lte=kwargs["create_at__lte"] + create_at__gte=kwargs["create_at__gte"], + create_at__lte=kwargs["create_at__lte"], ) - .values("username", "first_level_id") - .annotate(id=Max("id")) - .order_by("username") + .values("username", "first_level_id") + .annotate(id=Max("id")) + .order_by("username") ) - max_ids = [ticket['id'] for ticket in max_ids] + max_ids = [ticket["id"] for ticket in max_ids] user_organization = TicketOrganization.objects.filter(id__in=max_ids).only( "username", "first_level_name", "family" ) user_info = {} for user in user_organization: if user.username not in user_info: - user_info[user.username] = [{"name": user.first_level_name, "family": user.family}] + user_info[user.username] = [ + {"name": user.first_level_name, "family": user.family} + ] else: user_info[user.username].append( - {"name": user.first_level_name, "family": user.family}) - + {"name": user.first_level_name, "family": user.family} + ) + project_query = Q(project_key=project_key) if project_key else Q() ticket_info = ( - self.queryset.filter(project_query).filter(**kwargs).values("creator").annotate(count=Count("id")).order_by( - "-count")[:10] + self.queryset.filter(project_query) + .filter(**kwargs) + .values("creator") + .annotate(count=Count("id")) + .order_by("-count")[:10] ) top_creator = [ { @@ -415,13 +492,13 @@ def distribute_statistics(self, request): ticket_info = ( TicketOrganization.objects.filter(**kwargs) - .values(level_dict[level][0], level_dict[level][1]) - .annotate(count=Count("id")) - .order_by("-count") + .values(level_dict[level][0], level_dict[level][1]) + .annotate(count=Count("id")) + .order_by("-count") ) top_creator = [ - {"organization": ticket[level_dict[level][1]], "count": ticket["count"]} for ticket in - ticket_info[:10] + {"organization": ticket[level_dict[level][1]], "count": ticket["count"]} + for ticket in ticket_info[:10] ] return Response(top_creator) @@ -444,13 +521,18 @@ def get_time_params(request, params_key="create_at"): return time_params try: - time_params[params_key + "__gte"] = time_params[params_key + "__gte"] + " 00:00:00" - time_params[params_key + "__lte"] = time_params[params_key + "__lte"] + " 23:59:59" + time_params[params_key + "__gte"] = ( + time_params[params_key + "__gte"] + " 00:00:00" + ) + time_params[params_key + "__lte"] = ( + time_params[params_key + "__lte"] + " 23:59:59" + ) return time_params except KeyError: raise ValidationError( - _("日期范围输入有误,请重新输入,例如:{}__gte=2019-01-01, {}__lte=2019-01-02").format(params_key, - params_key) + _("日期范围输入有误,请重新输入,例如:{}__gte=2019-01-01, {}__lte=2019-01-02").format( + params_key, params_key + ) ) @staticmethod @@ -465,23 +547,27 @@ def get_month_params(queryset, request): try: if create_at_params.get("create_at__gte"): create_at_params["create_at__gte"] = datetime.strptime( - create_at_params["create_at__gte"], "%Y-%m") + create_at_params["create_at__gte"], "%Y-%m" + ) else: create_at_params["create_at__gte"] = queryset.first().create_at if create_at_params.get("create_at__lte"): create_at_params["create_at__lte"] = datetime.strptime( - create_at_params["create_at__lte"], "%Y-%m") + create_at_params["create_at__lte"], "%Y-%m" + ) else: create_at_params["create_at__lte"] = queryset.last().create_at except ValueError: raise ValidationError( - _("日期范围输入有误,请重新输入,例如:create_at__gte=2019-01, create_at__lte=2019-02")) + _("日期范围输入有误,请重新输入,例如:create_at__gte=2019-01, create_at__lte=2019-02") + ) # 左开右闭 - create_at_params["create_at__lt"] = create_at_params.pop("create_at__lte") + relativedelta( - months=1) + create_at_params["create_at__lt"] = create_at_params.pop( + "create_at__lte" + ) + relativedelta(months=1) return create_at_params @@ -512,10 +598,9 @@ def get_queryset_by_fields(query_set, request): ticket_ids = [] valid_tickets = [] for key, value in list(fields.items()): - tickets = TicketField.objects.filter(key=key, - _value__in=value.split(",")).values_list( - "ticket_id", flat=True - ) + tickets = TicketField.objects.filter( + key=key, _value__in=value.split(",") + ).values_list("ticket_id", flat=True) ticket_ids.append(tickets) if ticket_ids: @@ -539,8 +624,12 @@ def get_queryset_by_params(query_set, request): query_set = query_set.filter(sn__icontains=sn) if request.query_params.get("update_at__gte"): - update_at__gte = request.query_params.get("update_at__gte").replace(" ", " ") - query_set = query_set.filter(update_at__gt=update_at__gte).order_by("-update_at") + update_at__gte = request.query_params.get("update_at__gte").replace( + " ", " " + ) + query_set = query_set.filter(update_at__gt=update_at__gte).order_by( + "-update_at" + ) if request.query_params.get("is_draft"): is_draft = request.query_params.get("is_draft") @@ -582,7 +671,9 @@ def workflows(self, request, *args, **kwargs): workflow_id = request.query_params.get("workflow_id") if workflow_id: workflow = get_object_or_404(self.queryset, pk=workflow_id) - workflow_serializer = self.serializer_class(workflow, context={"query_type": "detail"}) + workflow_serializer = self.serializer_class( + workflow, context={"query_type": "detail"} + ) return Response(workflow_serializer.data) return self.list(request, *args, **kwargs) @@ -616,14 +707,18 @@ def ticket_category(self, request, *args, **kwargs): queryset = ( self.queryset.filter(**create_at_params) - .values("service_type") - .order_by("service_type") - .annotate(count=Count("id")) + .values("service_type") + .order_by("service_type") + .annotate(count=Count("id")) ) values = {value["service_type"]: value["count"] for value in queryset} return Response( - [{"service": service, "count": values.get(service, 0)} for service in SERVICE_LIST]) + [ + {"service": service, "count": values.get(service, 0)} + for service in SERVICE_LIST + ] + ) @action(detail=False, methods=["get"]) def month_ticket_category(self, request, *args, **kwargs): @@ -642,10 +737,10 @@ def month_ticket_category(self, request, *args, **kwargs): filter_result = list( queryset.filter(service_type=service_type) - .extra(select={"date": "date_format(create_at, '%%Y-%%m')"}) - .values("date", "service_type") - .order_by("date") - .annotate(count=Count("id")) + .extra(select={"date": "date_format(create_at, '%%Y-%%m')"}) + .values("date", "service_type") + .order_by("date") + .annotate(count=Count("id")) ) return Response(filter_result) @@ -663,11 +758,15 @@ def ticket_status(self, request, *args, **kwargs): queryset = queryset.filter(service_type=service_type) ticket_status = list( - TicketStatus.objects.filter(service_type=service_type).values_list("key", flat=True)) + TicketStatus.objects.filter(service_type=service_type).values_list( + "key", flat=True + ) + ) filter_result = list( - queryset.values("current_status").annotate(count=Count("current_status")).order_by( - "current_status") + queryset.values("current_status") + .annotate(count=Count("current_status")) + .order_by("current_status") ) filter_result_keys = [item["current_status"] for item in filter_result] @@ -675,7 +774,9 @@ def ticket_status(self, request, *args, **kwargs): filter_result.extend( [ {"current_status": status, "count": 0} - for status in list(set(ticket_status).difference(filter_result_keys)) + for status in list( + set(ticket_status).difference(filter_result_keys) + ) ] ) @@ -714,8 +815,9 @@ def month_close_ratio(self, request, *args, **kwargs): return Response() create_at_params = self.get_month_params(queryset, request) - month_list = get_month_list(create_at_params["create_at__gte"], - create_at_params["create_at__lt"]) + month_list = get_month_list( + create_at_params["create_at__gte"], create_at_params["create_at__lt"] + ) date_ratio = [{"date": month, "ratio": 1} for month in month_list] @@ -724,20 +826,22 @@ def month_close_ratio(self, request, *args, **kwargs): # 每月创建的单据 month_create_count = list( queryset.extra(select={"date": "date_format(create_at, '%%Y-%%m')"}) - .values("date", "service_type") - .order_by("date", "service_type") - .annotate(create_count=Count("create_at")) + .values("date", "service_type") + .order_by("date", "service_type") + .annotate(create_count=Count("create_at")) ) # 每月创建且在当月结束的单据 month_end_count = list( queryset.extra( select={"date": "date_format(create_at, '%%Y-%%m')"}, - where=['date_format(create_at, "%%Y-%%m") = date_format(end_at, "%%Y-%%m")'], + where=[ + 'date_format(create_at, "%%Y-%%m") = date_format(end_at, "%%Y-%%m")' + ], ) - .values("date", "service_type") - .order_by("date", "service_type") - .annotate(end_count=Count("create_at")) + .values("date", "service_type") + .order_by("date", "service_type") + .annotate(end_count=Count("create_at")) ) # sql: # SELECT (date_format(create_at, '%Y-%m')) AS `date`, @@ -755,9 +859,9 @@ def month_close_ratio(self, request, *args, **kwargs): # 整体 whole_month_create_count = list( queryset.extra(select={"date": "date_format(create_at, '%%Y-%%m')"}) - .values("date") - .order_by("date") - .annotate(create_count=Count("create_at")) + .values("date") + .order_by("date") + .annotate(create_count=Count("create_at")) ) # sql: # SELECT (date_format(create_at, '%Y-%m')) AS `date`, @@ -776,11 +880,13 @@ def month_close_ratio(self, request, *args, **kwargs): whole_month_end_count = list( queryset.extra( select={"date": "date_format(create_at, '%%Y-%%m')"}, - where=['date_format(create_at, "%%Y-%%m") = date_format(end_at, "%%Y-%%m")'], + where=[ + 'date_format(create_at, "%%Y-%%m") = date_format(end_at, "%%Y-%%m")' + ], ) - .values("date") - .order_by("date") - .annotate(end_count=Count("create_at")) + .values("date") + .order_by("date") + .annotate(end_count=Count("create_at")) ) for item in whole_month_end_count: item.update({"service_type": "whole"}) @@ -788,10 +894,15 @@ def month_close_ratio(self, request, *args, **kwargs): end_count = month_end_count + whole_month_end_count for create in create_count: for end in end_count: - if create["service_type"] == end["service_type"] and create["date"] == end["date"]: + if ( + create["service_type"] == end["service_type"] + and create["date"] == end["date"] + ): try: create["ratio"] = "%.2f" % ( - (float(end["end_count"]) / float(create["create_count"])) * 100) + (float(end["end_count"]) / float(create["create_count"])) + * 100 + ) except ZeroDivisionError: create["ratio"] = "%.2f" % 100 @@ -803,16 +914,27 @@ def month_close_ratio(self, request, *args, **kwargs): if set(month_list).difference([value["date"] for value in values]): values.extend( [ - {"date": month, "ratio": 100, "create_count": 0, "service_type": key, } - for month in set(month_list).difference([value["date"] for value in values]) + { + "date": month, + "ratio": 100, + "create_count": 0, + "service_type": key, + } + for month in set(month_list).difference( + [value["date"] for value in values] + ) ] ) values.sort(key=lambda x: x["date"]) if set(SERVICE_LIST + ["whole"]).difference(list(filter_datas.keys())): filter_datas.update( - {service: date_ratio for service in - set(SERVICE_LIST + ["whole"]).difference(list(filter_datas.keys()))} + { + service: date_ratio + for service in set(SERVICE_LIST + ["whole"]).difference( + list(filter_datas.keys()) + ) + } ) return Response(filter_datas) @@ -821,7 +943,8 @@ def month_close_ratio(self, request, *args, **kwargs): detail=False, methods=["get"], queryset=TicketEventLog.objects.all().exclude( - Q(message__in=[u"流程结束.", u"流程开始."]) | Q(source=SYSTEM_OPERATE)), + Q(message__in=["流程结束.", "流程开始."]) | Q(source=SYSTEM_OPERATE) + ), ) def ticket_processor_rank(self, request, *args, **kwargs): """工单处理量/认领量/派单量""" @@ -831,25 +954,25 @@ def ticket_processor_rank(self, request, *args, **kwargs): queryset = self.queryset.filter(**operate_at_params) process_list = ( queryset.filter(type=TRANSITION_OPERATE) - .values("operator") - .order_by("operator") - .annotate(count=Count("id")) - .order_by("-count")[:10] + .values("operator") + .order_by("operator") + .annotate(count=Count("id")) + .order_by("-count")[:10] ) claim_list = ( queryset.filter(type=CLAIM_OPERATE) - .values("operator") - .order_by("operator") - .annotate(count=Count("id")) - .order_by("-count")[:10] + .values("operator") + .order_by("operator") + .annotate(count=Count("id")) + .order_by("-count")[:10] ) distribute_list = ( queryset.filter(type=DISTRIBUTE_OPERATE) - .values("operator") - .order_by("operator") - .annotate(count=Count("id")) - .order_by("-count")[:10] + .values("operator") + .order_by("operator") + .annotate(count=Count("id")) + .order_by("-count")[:10] ) for item in process_list: @@ -862,7 +985,12 @@ def ticket_processor_rank(self, request, *args, **kwargs): item.update({"operator": transform_single_username(item["operator"])}) return Response( - {"processors": process_list, "claimers": claim_list, "distributors": distribute_list, }) + { + "processors": process_list, + "claimers": claim_list, + "distributors": distribute_list, + } + ) @action(detail=False, methods=["get"]) def ticket_score(self, request, *args, **kwargs): @@ -873,10 +1001,12 @@ def ticket_score(self, request, *args, **kwargs): return Response() create_at_params = self.get_month_params(queryset, request) - create_at_params["create_at__gte"] = create_at_params["create_at__gte"].strftime( - "%Y-%m-%d %H:%M:%S") + create_at_params["create_at__gte"] = create_at_params[ + "create_at__gte" + ].strftime("%Y-%m-%d %H:%M:%S") create_at_params["create_at__lt"] = create_at_params["create_at__lt"].strftime( - "%Y-%m-%d %H:%M:%S") + "%Y-%m-%d %H:%M:%S" + ) service_type = request.query_params.get("service_type", "") if service_type and service_type not in SERVICE_LIST: @@ -892,7 +1022,9 @@ def ticket_score(self, request, *args, **kwargs): ) else: records = dictfetchall( - connection, ticket_score_sql, create_at_params["create_at__gte"], + connection, + ticket_score_sql, + create_at_params["create_at__gte"], create_at_params["create_at__lt"], ) @@ -908,12 +1040,15 @@ def ticket_time(self, request, *args, **kwargs): return Response() create_at_params = self.get_month_params(queryset, request) - month_list = get_month_list(create_at_params["create_at__gte"], - create_at_params["create_at__lt"]) - create_at_params["create_at__gte"] = create_at_params["create_at__gte"].strftime( - "%Y-%m-%d %H:%M:%S") + month_list = get_month_list( + create_at_params["create_at__gte"], create_at_params["create_at__lt"] + ) + create_at_params["create_at__gte"] = create_at_params[ + "create_at__gte" + ].strftime("%Y-%m-%d %H:%M:%S") create_at_params["create_at__lt"] = create_at_params["create_at__lt"].strftime( - "%Y-%m-%d %H:%M:%S") + "%Y-%m-%d %H:%M:%S" + ) service_type = request.query_params.get("service_type", "") if service_type and service_type not in SERVICE_LIST: @@ -936,11 +1071,15 @@ def ticket_time(self, request, *args, **kwargs): ) else: solve_records = dictfetchall( - connection, solve_sql, create_at_params["create_at__gte"], + connection, + solve_sql, + create_at_params["create_at__gte"], create_at_params["create_at__lt"], ) response_records = dictfetchall( - connection, response_sql, create_at_params["create_at__gte"], + connection, + response_sql, + create_at_params["create_at__gte"], create_at_params["create_at__lt"], ) @@ -948,23 +1087,30 @@ def ticket_time(self, request, *args, **kwargs): solve_records.extend( [ {"date": month, "ratio": 0, "count": 0, "total": 0} - for month in - set(month_list).difference([record["date"] for record in solve_records]) + for month in set(month_list).difference( + [record["date"] for record in solve_records] + ) ] ) if set(month_list).difference([record["date"] for record in response_records]): response_records.extend( [ {"date": month, "ratio": 0, "count": 0, "total": 0} - for month in - set(month_list).difference([record["date"] for record in response_records]) + for month in set(month_list).difference( + [record["date"] for record in response_records] + ) ] ) solve_records.sort(key=lambda x: x["date"]) response_records.sort(key=lambda x: x["date"]) - return Response({"solve_records": solve_records, "response_records": response_records, }) + return Response( + { + "solve_records": solve_records, + "response_records": response_records, + } + ) @action(detail=False, methods=["get"]) def new_tickets(self, request, *args, **kwargs): @@ -981,10 +1127,10 @@ def new_tickets(self, request, *args, **kwargs): filter_result = list( queryset.extra(select={"day": "date(create_at)"}) - .values("day") - .distinct() - .order_by("day") - .annotate(count=Count("create_at")) + .values("day") + .distinct() + .order_by("day") + .annotate(count=Count("create_at")) ) if not create_at_params: @@ -996,15 +1142,22 @@ def new_tickets(self, request, *args, **kwargs): ) else: days = get_days( - begin=datetime.strptime(create_at_params["create_at__gte"].split(" ")[0], - "%Y-%m-%d"), - end=datetime.strptime(create_at_params["create_at__lte"].split(" ")[0], "%Y-%m-%d"), + begin=datetime.strptime( + create_at_params["create_at__gte"].split(" ")[0], "%Y-%m-%d" + ), + end=datetime.strptime( + create_at_params["create_at__lte"].split(" ")[0], "%Y-%m-%d" + ), ) filter_result_days = [item["day"] for item in filter_result] if set(days).difference(filter_result_days): - filter_result.extend([{"day": day, "count": 0} for day in - list(set(days).difference(filter_result_days))]) + filter_result.extend( + [ + {"day": day, "count": 0} + for day in list(set(days).difference(filter_result_days)) + ] + ) filter_result.sort(key=lambda x: x["day"]) return Response(filter_result) diff --git a/itsm/workflow/apps.py b/itsm/workflow/apps.py index e57dff17a..55705bfdb 100644 --- a/itsm/workflow/apps.py +++ b/itsm/workflow/apps.py @@ -73,14 +73,16 @@ def fix_migrate_error(sender, **kwarg): dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") if migration not in rows: cursor.execute( - 'INSERT INTO `django_migrations` (`app`, `name`, `applied`) VALUES ("{}", "{}", "{}");'.format( # noqa + "INSERT INTO `django_migrations` (`app`, `name`, `applied`) " + 'VALUES ("{}", "{}", "{}");'.format( # noqa "workflow", migration, dt ) ) else: if value not in rows: cursor.execute( - 'INSERT INTO `django_migrations` (`app`, `name`, `applied`) VALUES ("{}", "{}", "{}");'.format( # noqa + "INSERT INTO `django_migrations` (`app`, `name`, `applied`) " + 'VALUES ("{}", "{}", "{}");'.format( # noqa "workflow", value, dt ) ) diff --git a/sops_proxy/urls.py b/sops_proxy/urls.py index 4630e7606..738faf4d2 100644 --- a/sops_proxy/urls.py +++ b/sops_proxy/urls.py @@ -25,12 +25,12 @@ from django.conf.urls import url -from sops_proxy.views import dispatch_query, dispatch_static, SopsProxy +from sops_proxy.views import SopsProxy urlpatterns = [ # 插件静态资源(jsonp)转发 # url(r"^static/(?P.*)$", dispatch_static), # 插件请求(ajax)转发 # url(r"^(?P.*)$", dispatch_query), - url(r'^(?P.*)$', SopsProxy.as_view()), + url(r"^(?P.*)$", SopsProxy.as_view()), ] From 981be6b396f5c67d0c8d4eb5445a991f982fbd05 Mon Sep 17 00:00:00 2001 From: cheynechen Date: Wed, 4 Sep 2024 16:48:08 +0800 Subject: [PATCH 2/2] =?UTF-8?q?minor:=20=E4=BB=A3=E7=A0=81=E8=B4=A8?= =?UTF-8?q?=E9=87=8F=E4=BC=98=E5=8C=96=20--story=3D119534560?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iam/contrib/tastypie/authorization.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/iam/contrib/tastypie/authorization.py b/iam/contrib/tastypie/authorization.py index 16e749c0f..e547497f7 100644 --- a/iam/contrib/tastypie/authorization.py +++ b/iam/contrib/tastypie/authorization.py @@ -42,11 +42,7 @@ def __init__( self.read_action = read_action self.update_action = update_action self.delete_action = delete_action - - if filter_key_mapping is None: - self.filter_key_mapping = {} - else: - self.filter_key_mapping = filter_key_mapping + self.filter_key_mapping = filter_key_mapping or {} @abc.abstractmethod def get_subject(self, bundle):