From cf9408449b4c5e53f768d8b9ec104e466fbc8239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80=C3=AE=C3=97=C3=9A=C3=95=C3=B1?= Date: Fri, 22 Jul 2022 17:59:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/api_v1/endpoints/forms.py | 182 +++++++++++++++++++++++++++- crud/crud_interview_record.py | 9 +- crud/crud_jobs.py | 12 +- models/interview_zsgc.py | 103 ++++++++++++++-- 接口文档/hr工作量模板接口文档.txt | 68 +++++++++++ 接口文档/候选人明细模板接口文档.txt | 50 ++++++++ 接口文档/招聘漏斗模板接口文档.txt | 24 ++++ 7 files changed, 435 insertions(+), 13 deletions(-) create mode 100644 接口文档/hr工作量模板接口文档.txt create mode 100644 接口文档/候选人明细模板接口文档.txt create mode 100644 接口文档/招聘漏斗模板接口文档.txt diff --git a/api/api_v1/endpoints/forms.py b/api/api_v1/endpoints/forms.py index d5712de..b7519f4 100644 --- a/api/api_v1/endpoints/forms.py +++ b/api/api_v1/endpoints/forms.py @@ -212,9 +212,35 @@ async def man_mass_form( return schemas.Msg(code=200, msg='ok', data=res_msg) +# 候选人明细报表 +@router.post("/man_info_form") +async def man_info_form( + request: Request, + interview: InterviewDo = Depends(InterviewDo), + db: CKDrive = Depends(get_ck_db), +) -> schemas.Msg: + """ 候选人明细报表 """ + await interview.init() + res = interview.get_man_info_form_sql() + sql = res['sql'] + data = await db.execute(sql) + if not data: + return schemas.Msg(code=-9, msg='无数据', data=None) + for key, interview_data in data.items(): + i_work_list = interview_data.get('work_list', '') + if i_work_list: + data_work = eval(i_work_list[0]) + i_work_for = data_work.get('name', '') + if i_work_for: + data[key]['work_for'] = i_work_for + continue + data[key]['work_for'] = '' + return schemas.Msg(code=200, msg='ok', data=data) + + # 职位阶段数据报表 @router.post("/every_stage_form") -async def owner_form( +async def every_stage_form( request: Request, interview: InterviewDo = Depends(InterviewDo), ck_db: CKDrive = Depends(get_ck_db), @@ -265,9 +291,122 @@ async def owner_form( return schemas.Msg(code=200, msg='ok', data=res_msg) +# hr工作量报表 +@router.post("/hr_works_form") +async def hr_works_form( + request: Request, + interview: InterviewDo = Depends(InterviewDo), + ck_db: CKDrive = Depends(get_ck_db), + db: AsyncIOMotorDatabase = Depends(get_database) +) -> schemas.Msg: + """ hr工作量报表 """ + await interview.init() + res = interview.get_hr_works_form_sql() + sql = res['sql'] + data = await ck_db.execute(sql) + res_msg = {} + job_name_sector = {} + hr_names = [] # 查询面试数据 + job_ids = [] + for ck_data in data.values(): + ck_job_id = ck_data['job_id'] + hr_name = ck_data['hr_name'] + + if hr_name not in hr_names: + hr_names.append(hr_name) + + job_data = await crud.jobs.find_job_name(db, ck_job_id) + job_name = job_data['job_name'] # 职位 + job_sector = job_data['job_sector'] # 部门 + job_name_sector.update({ + job_name: job_sector + }) + + # 未录入职位 + if ck_job_id not in job_ids: + job_ids.append(ck_job_id) + res_msg[job_name] = {hr_name: { + '初筛': 0, + '复筛': 0, + '面试': 0, + '创建面试': 0, + '面试签到': 0, + 'offer': 0, + '待入职': 0, + '已入职': 0 + }} + # 已录入职位 + else: + if hr_name not in res_msg[job_name]: + res_msg[job_name][hr_name] = { + '初筛': 0, + '复筛': 0, + '面试': 0, + '创建面试': 0, + '面试签到': 0, + 'offer': 0, + '待入职': 0, + '已入职': 0 + } + + stage = ck_data['interview_stage'] + if stage >= 1: + res_msg[job_name][hr_name]['初筛'] += 1 + if stage >= 2: + res_msg[job_name][hr_name]['复筛'] += 1 + if stage >= 3: + res_msg[job_name][hr_name]['面试'] += 1 + if stage >= 4: + res_msg[job_name][hr_name]['offer'] += 1 + if stage >= 5: + res_msg[job_name][hr_name]['待入职'] += 1 + if stage >= 7: + res_msg[job_name][hr_name]['已入职'] += 1 + interview_records = await crud.interview_record.find_job_some(db, hr_names) + for record in interview_records: + record_job_name = record['job_names'] + record_hr_name = record['hr_name'] + record_interview_sign = record['interview_sign'] + if record_job_name in res_msg: + if record_hr_name not in res_msg[record_job_name]: + res_msg[record_job_name][record_hr_name] = { + '初筛': 0, + '复筛': 0, + '面试': 0, + '创建面试': 0, + '面试签到': 0, + 'offer': 0, + '待入职': 0, + '已入职': 0 + } + res_msg[record_job_name][record_hr_name]['创建面试'] += 1 + if record_interview_sign: + res_msg[record_job_name][record_hr_name]['面试签到'] += 1 + for job_name1, msg_data in res_msg.items(): + count_data = { + '初筛': 0, + '复筛': 0, + '面试': 0, + '创建面试': 0, + '面试签到': 0, + 'offer': 0, + '待入职': 0, + '已入职': 0 + } + for key, value_data in msg_data.items(): + for true_key, num in value_data.items(): + count_data[true_key] += num + res_msg[job_name1].update({'总计': count_data}) + res_data = { + 'msg': res_msg, + 'job_sector': job_name_sector + } + return schemas.Msg(code=200, msg='ok', data=res_data) + + # 职位阶段通过率报表 @router.post("/stage_success_form") -async def owner_form( +async def stage_success_form( request: Request, interview: InterviewDo = Depends(InterviewDo), ck_db: CKDrive = Depends(get_ck_db), @@ -354,6 +493,44 @@ async def owner_form( return schemas.Msg(code=200, msg='ok', data=res_msg) +# 招聘漏斗报表 +@router.post("/interview_funnel_form") +async def interview_funnel_form( + request: Request, + interview: InterviewDo = Depends(InterviewDo), + ck_db: CKDrive = Depends(get_ck_db) +) -> schemas.Msg: + """ 招聘漏斗报表 """ + await interview.init() + res = interview.get_hr_works_form_sql() + sql = res['sql'] + data = await ck_db.execute(sql) + res_msg = { + '初筛': 0, + '复筛': 0, + '面试': 0, + 'offer': 0, + '待入职': 0, + '已入职': 0 + } + for i_data in data.values(): + stage = i_data['interview_stage'] + if stage >= 1: + res_msg['初筛'] += 1 + if stage >= 2: + res_msg['复筛'] += 1 + if stage >= 3: + res_msg['面试'] += 1 + if stage >= 4: + res_msg['offer'] += 1 + if stage >= 5: + res_msg['待入职'] += 1 + if stage >= 7: + res_msg['已入职'] += 1 + + return schemas.Msg(code=200, msg='ok', data=res_msg) + + # 渠道质量报表 @router.post("/owner_form") async def owner_form( @@ -419,3 +596,4 @@ async def owner_form( res_msg[key]['work_chance'] = work_chance return schemas.Msg(code=200, msg='ok', data=res_msg) + diff --git a/crud/crud_interview_record.py b/crud/crud_interview_record.py index 9ebf29e..56c2818 100644 --- a/crud/crud_interview_record.py +++ b/crud/crud_interview_record.py @@ -5,11 +5,16 @@ from crud.base import CRUDBase __all__ = 'interview_record', -class CRUDJobs(CRUDBase): +class CRUDInterview(CRUDBase): # 获取所有面试数据 async def all_field(self, db: AsyncIOMotorDatabase): return await self.find_many(db, {}, {'_id': 0}) + # 获取对应job_id的名字,以及部门, 招聘数量 + async def find_job_some(self, db: AsyncIOMotorDatabase, hr_names): + + return await self.find_many(db, {'hr_name': {"$in": hr_names}}, + {'_id': 0, 'job_names': 1, 'hr_name': 1, 'interview_sign': 1, 'job_id': 1}) # 获取所有对应条件面试数据 # async def all_fields(self, db: AsyncIOMotorDatabase, data_in: schemas.Jobs): # if data_in == None: @@ -36,4 +41,4 @@ class CRUDJobs(CRUDBase): await self.insert_one(db, data_in.dict()) -interview_record = CRUDJobs('interview_record') +interview_record = CRUDInterview('interview_record') diff --git a/crud/crud_jobs.py b/crud/crud_jobs.py index 43384bf..9112477 100644 --- a/crud/crud_jobs.py +++ b/crud/crud_jobs.py @@ -38,13 +38,21 @@ class CRUDJobs(CRUDBase): # 插入一条新的职位数据 async def insert_job(self, db: AsyncIOMotorDatabase, data_in: schemas.Ins_Job): - await self.insert_one(db, data_in.dict()) # 获取对应jobid的信息 async def find_job(self, db: AsyncIOMotorDatabase, job_id): - return await self.find_one(db, {'job_id': job_id}) + # 获取对应job_id的部门和名称 + async def find_job_name(self, db: AsyncIOMotorDatabase, job_id): + return await self.find_one(db, {'job_id': job_id}, {'_id': 0, 'job_name': 1, 'job_sector': 1}) + + # 获取对应job_id的名字,以及部门, 招聘数量 + async def find_job_some(self, db: AsyncIOMotorDatabase, job_ids): + return await self.find_many(db, {'job_id': {"$in": job_ids}}, + {'_id': 0, 'job_name': 1, 'job_sector': 1, 'job_num': 1, 'job_id': 1, + 'principal': 1}) + jobs = CRUDJobs('jobs') diff --git a/models/interview_zsgc.py b/models/interview_zsgc.py index 6ff27fe..8513d36 100644 --- a/models/interview_zsgc.py +++ b/models/interview_zsgc.py @@ -412,9 +412,9 @@ class InterviewDo: end_time = self.where.get('end_time', '') sql = f"select {findStr} from HR.resumes where {whereStr}" if start_time: - sql += f" and toDate(event_time) >= `{start_time}`" + sql += f" and toDate(event_time) >= {start_time}" if end_time: - sql += f" and toDate(event_time) <= `{end_time}`" + sql += f" and toDate(event_time) <= {end_time}" # 没有日期条件 else: sql = f"select {findStr} from HR.resumes where {whereStr}" @@ -424,6 +424,51 @@ class InterviewDo: 'sql': sql } + # 分组报表候选人明细查询 + def get_man_info_form_sql(self): + whereStr = '' + findStr = '' + # 查询字段 + self.find_column = ["uid", "age", "gender", "name", "education", "school", "work_exp", "job_name", "account", + "work_list", "graduate_time", "phone"] + for fstr in self.find_column: + findStr += fstr + ', ' + is_date = 0 + for key, value in self.where.items(): + if key in ['start_time', 'end_time']: + is_date = 1 + continue + if isinstance(value, str): + whereStr += str(key) + ' = ' + "'" + value + "'" + ' ' + continue + whereStr += str(key) + ' = ' + str(value) + ' ' + whereStr = whereStr.strip() + findStr = findStr.strip().strip(',') + + # 有日期条件 + if is_date: + start_time = self.where.get('start_time', '') + end_time = self.where.get('end_time', '') + if whereStr: + sql = f"select {findStr} from HR.resumes where {whereStr}" + else: + sql = f"select {findStr} from HR.resumes" + if start_time: + sql += f" and toDate(event_time) >= {start_time}" + if end_time: + sql += f" and toDate(event_time) <= {end_time}" + # 没有日期条件 + else: + if whereStr: + sql = f"select {findStr} from HR.resumes where {whereStr}" + else: + sql = f"select {findStr} from HR.resumes" + + print(sql) + return { + 'sql': sql + } + # 渠道质量sql def get_owner_form_sql(self): findStr = '' @@ -454,9 +499,9 @@ class InterviewDo: else: sql = f"select {findStr} from HR.resumes where '1'" if start_time: - sql += f" and toDate(event_time) >= `{start_time}`" + sql += f" and toDate(event_time) >= {start_time}" if end_time: - sql += f" and toDate(event_time) <= `{end_time}`" + sql += f" and toDate(event_time) <= {end_time}" # 没有日期条件 else: if whereStr: @@ -468,7 +513,7 @@ class InterviewDo: 'sql': sql } - # 渠道质量sql + # 阶段数据sql def get_every_stage_form_sql(self, job_ids): findStr = '' whereStr = '' @@ -498,9 +543,9 @@ class InterviewDo: else: sql = f"select {findStr} from HR.resumes where job_id in {job_ids}" if start_time: - sql += f" and toDate(event_time) >= `{start_time}`" + sql += f" and toDate(event_time) >= {start_time}" if end_time: - sql += f" and toDate(event_time) <= `{end_time}`" + sql += f" and toDate(event_time) <= {end_time}" # 没有日期条件 else: if whereStr: @@ -512,6 +557,50 @@ class InterviewDo: 'sql': sql } + # hr工作量sql + def get_hr_works_form_sql(self): + findStr = '' + whereStr = '' + # 查询字段 + self.find_column = ["name", "interview_stage", "job_id", 'hr_name'] + for fstr in self.find_column: + findStr += fstr + ', ' + is_date = 0 + if self.where: + for key, value in self.where.items(): + if key in ['start_time', 'end_time']: + is_date = 1 + continue + if isinstance(value, str): + whereStr += str(key) + ' = ' + "'" + value + "'" + ' ' + continue + whereStr += str(key) + ' = ' + str(value) + ' ' + whereStr = whereStr.strip() + findStr = findStr.strip().strip(',') + + # 有日期条件 + if is_date: + start_time = self.where.get('start_time', '') + end_time = self.where.get('end_time', '') + if whereStr: + sql = f"select {findStr} from HR.resumes where {whereStr}" + else: + sql = f"select {findStr} from HR.resumes" + if start_time: + sql += f" and toDate(event_time) >= {start_time}" + if end_time: + sql += f" and toDate(event_time) <= {end_time}" + # 没有日期条件 + else: + if whereStr: + sql = f"select {findStr} from HR.resumes where {whereStr}" + else: + sql = f"select {findStr} from HR.resumes" + print(sql) + return { + 'sql': sql + } + if __name__ == '__main__': find = re.search(r"\d+\.?\d*", "18岁") diff --git a/接口文档/hr工作量模板接口文档.txt b/接口文档/hr工作量模板接口文档.txt new file mode 100644 index 0000000..db55ae3 --- /dev/null +++ b/接口文档/hr工作量模板接口文档.txt @@ -0,0 +1,68 @@ +·: /api/v1/forms/hr_works_form + +: +{ + "data_in": "", # ɶΪ + "interview_query": {}, + "find_column": [ + ] +} + + +ֵ: +{ + "code": 200, + "msg": "ok", + "data": { + "msg": { + "python": { # ְλhrӦ{'':{}, 'ΰ':{},...} + "": { + "ɸ": 1, + "ɸ": 1, + "": 1, + "": 2, + "ǩ": 1, + "offer": 1, + "ְ": 1, + "ְ": 1 + }, + "ܼ": { # ְλݺϼ + "ɸ": 1, + "ɸ": 1, + "": 1, + "": 2, + "ǩ": 1, + "offer": 1, + "ְ": 1, + "ְ": 1 + } + }, + "߼ϷԹʦ": { + "": { + "ɸ": 1, + "ɸ": 1, + "": 1, + "": 0, + "ǩ": 0, + "offer": 1, + "ְ": 1, + "ְ": 0 + }, + "ܼ": { + "ɸ": 1, + "ɸ": 1, + "": 1, + "": 0, + "ǩ": 0, + "offer": 1, + "ְ": 1, + "ְ": 0 + } + } + }, + "job_sector": { # ְλӦ + "python": "ƽ̨", + "߼ϷԹʦ": "з" + } + } +} \ No newline at end of file diff --git a/接口文档/候选人明细模板接口文档.txt b/接口文档/候选人明细模板接口文档.txt new file mode 100644 index 0000000..468d59a --- /dev/null +++ b/接口文档/候选人明细模板接口文档.txt @@ -0,0 +1,50 @@ +·: /api/v1/forms/man_info_form + +: +{ + "data_in": "", # ɶΪ + "interview_query": {}, + "find_column": [ + ] +} + + +ֵ: +{ + "code": 200, + "msg": "ok", + "data": { + "0": { + "uid": "3ae63e44e24a38", + "age": 29, # + "gender": "", # Ա + "name": "", # + "education": 2, # ѧ 1-5 ר,,о,ʿ,˶ʿ + "school": "人֯ѧ", # ҵѧУ + "work_exp": 6, # + "job_name": "Androidʦ", # ӦƸְλ + "account": "", # ڵ + "work_list": [ + "{'name': 'ʿƼ޹˾', 'time': '2016.08-2017.6'}" + ], + "graduate_time": "", # ҵʱ + "phone": "15902799433", # 绰 + "work_for": "ʿƼ޹˾" # ˾ + }, + "1": { + "uid": "3ae3f4d54e03ca", + "age": 20, + "gender": "", + "name": "³", + "education": 2, + "school": "ѧ", + "work_exp": 0, + "job_name": "androidʦ", + "account": "Ƹ", + "work_list": [], + "graduate_time": "", + "phone": "15827578935", + "work_for": "" + } + } +} \ No newline at end of file diff --git a/接口文档/招聘漏斗模板接口文档.txt b/接口文档/招聘漏斗模板接口文档.txt new file mode 100644 index 0000000..538b854 --- /dev/null +++ b/接口文档/招聘漏斗模板接口文档.txt @@ -0,0 +1,24 @@ +·: /api/v1/forms/interview_funnel_form + +: +{ + "data_in": "", # ɶΪ + "interview_query": {}, + "find_column": [ + ] +} + + +ֵ: +{ + "code": 200, + "msg": "ok", + "data": { # © + "ɸ": 2, + "ɸ": 2, + "": 2, + "offer": 2, + "ְ": 2, + "ְ": 1 + } +} \ No newline at end of file