From 4b166150b83d2d73ed1fd807f22e89ae543d8b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80=C3=AE=C3=97=C3=9A=C3=95=C3=B1?= Date: Mon, 25 Jul 2022 18:13:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=9B=E8=81=98=E8=B6=8B=E5=8A=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/api_v1/api.py | 2 + api/api_v1/endpoints/forms.py | 64 ++++++++++++++++++--- api/api_v1/endpoints/worker_info.py | 64 +++++++++++++++++++++ crud/__init__.py | 1 + crud/crud_worker_info.py | 34 +++++++++++ models/interview_zsgc.py | 89 +++++++++++++++++++++++++---- schemas/__init__.py | 1 + schemas/worker.py | 14 +++++ utils/func.py | 16 +++++- 接口文档/招聘趋势分析接口文档.txt | 49 ++++++++++++++++ 10 files changed, 316 insertions(+), 18 deletions(-) create mode 100644 api/api_v1/endpoints/worker_info.py create mode 100644 crud/crud_worker_info.py create mode 100644 schemas/worker.py create mode 100644 接口文档/招聘趋势分析接口文档.txt diff --git a/api/api_v1/api.py b/api/api_v1/api.py index 862e772..a0e3a97 100644 --- a/api/api_v1/api.py +++ b/api/api_v1/api.py @@ -11,6 +11,7 @@ from .endpoints import query from .endpoints import xquery from .endpoints import interview from .endpoints import forms +from .endpoints import worker_info from .endpoints import data_auth from .endpoints import event_mana from .endpoints import test @@ -43,4 +44,5 @@ api_router.include_router(user_label.router, tags=["用户标签"], prefix='/use api_router.include_router(interview.router, tags=["面试数据管理"], prefix='/itr') api_router.include_router(forms.router, tags=["hr报表模板管理"], prefix='/forms') +api_router.include_router(worker_info.router, tags=["入职人员信息管理"], prefix='/worker') diff --git a/api/api_v1/endpoints/forms.py b/api/api_v1/endpoints/forms.py index b7519f4..914a6fe 100644 --- a/api/api_v1/endpoints/forms.py +++ b/api/api_v1/endpoints/forms.py @@ -8,6 +8,7 @@ from motor.motor_asyncio import AsyncIOMotorDatabase from utils.dingding import get_redis_alluid, send_dates from utils.jianli import get_resume +from utils.func import get_every_days import crud, schemas from datetime import datetime from core.configuration import * @@ -271,6 +272,8 @@ async def every_stage_form( res = interview.get_every_stage_form_sql(job_ids) sql = res['sql'] data = await ck_db.execute(sql) + if not data: + return schemas.Msg(code=-9, msg='无数据', data=None) for ck_data in data.values(): # 职位id不存在跳过 if ck_data['job_id'] not in job_id_to_name: @@ -304,6 +307,8 @@ async def hr_works_form( res = interview.get_hr_works_form_sql() sql = res['sql'] data = await ck_db.execute(sql) + if not data: + return schemas.Msg(code=-9, msg='无数据', data=None) res_msg = {} job_name_sector = {} hr_names = [] # 查询面试数据 @@ -437,6 +442,8 @@ async def stage_success_form( res = interview.get_every_stage_form_sql(job_ids) sql = res['sql'] data = await ck_db.execute(sql) + if not data: + return schemas.Msg(code=-9, msg='无数据', data=None) for ck_data in data.values(): # 职位id不存在跳过 if ck_data['job_id'] not in job_id_to_name: @@ -505,14 +512,16 @@ async def interview_funnel_form( res = interview.get_hr_works_form_sql() sql = res['sql'] data = await ck_db.execute(sql) + if not data: + return schemas.Msg(code=-9, msg='无数据', data=None) res_msg = { - '初筛': 0, - '复筛': 0, - '面试': 0, - 'offer': 0, - '待入职': 0, - '已入职': 0 - } + '初筛': 0, + '复筛': 0, + '面试': 0, + 'offer': 0, + '待入职': 0, + '已入职': 0 + } for i_data in data.values(): stage = i_data['interview_stage'] if stage >= 1: @@ -543,6 +552,8 @@ async def owner_form( res = interview.get_owner_form_sql() sql = res['sql'] data = await db.execute(sql) + if not data: + return schemas.Msg(code=-9, msg='无数据', data=None) # 简历渠道 owner_dict = {1: '前程无忧', 2: '人才库', 3: '智联招聘', 4: 'Boss直聘', 5: '58同城'} chk_data = { @@ -597,3 +608,42 @@ async def owner_form( return schemas.Msg(code=200, msg='ok', data=res_msg) + +# 招聘趋势分析报表 +@router.post("/interview_trend_form") +async def interview_trend_form( + request: Request, + interview: InterviewDo = Depends(InterviewDo), + ck_db: CKDrive = Depends(get_ck_db) +) -> schemas.Msg: + """ 招聘趋势分析报表 """ + await interview.init() + res = interview.get_trend_form_sql() + sql = res['sql'] + data = await ck_db.execute(sql) + if not data: + return schemas.Msg(code=-9, msg='无数据', data=None) + sdate = res['sdate'] + edate = res['edate'] + days = get_every_days(sdate, edate) + res_msg = {} + for i in days: + res_msg[str(i)] = { + '初筛简历数': 0, + '初筛': 0, + '创建面试的申请数': 0 + } + for chk_data in data.values(): + chk_date = datetime.strftime(chk_data['date'], '%Y-%m-%d') + if chk_date not in res_msg: + continue + chk_stage = chk_data['interview_stage'] + count_num = chk_data['value'] + if chk_stage >= 1: + res_msg[chk_date]['初筛简历数'] += 1 * count_num + if chk_stage >= 2: + res_msg[chk_date]['初筛'] += 1 * count_num + if chk_stage >= 3: + res_msg[chk_date]['创建面试的申请数'] += 1 * count_num + + return schemas.Msg(code=200, msg='ok', data=res_msg) diff --git a/api/api_v1/endpoints/worker_info.py b/api/api_v1/endpoints/worker_info.py new file mode 100644 index 0000000..8afd12f --- /dev/null +++ b/api/api_v1/endpoints/worker_info.py @@ -0,0 +1,64 @@ +import operator +import os +import re +import pandas as pd +from copy import deepcopy +from fastapi import APIRouter, Depends, Request, File, UploadFile +from motor.motor_asyncio import AsyncIOMotorDatabase + +from utils.dingding import get_redis_alluid, send_dates +from utils.jianli import get_resume +import crud, schemas +from datetime import datetime +from core.configuration import * +from db import get_database +from db.ckdb import get_ck_db, CKDrive + +from models.interview_zsgc import InterviewDo +from utils import get_time, qujian_time, Download_xlsx, send_str_mail + +router = APIRouter() + + +# 增加入职人员 +@router.post("/add_worker") +async def add_worker( + request: Request, + data_in: schemas.WorkerQuery, + db: AsyncIOMotorDatabase = Depends(get_database) +) -> schemas.Msg: + """ 增加入职人员信息 """ + + data = await crud.worker_info.insert_worker(db, data_in.data_in) + + return schemas.Msg(code=200, msg='ok', data=data) + + +# 查询入职人员信息 +@router.post("/find_worker") +async def add_worker( + request: Request, + data_in: schemas.WorkerQuery, + db: AsyncIOMotorDatabase = Depends(get_database) +) -> schemas.Msg: + """ 查询入职人员信息 """ + + data = await crud.worker_info.find_worker_some(db, findlist=data_in.find_filed) + + return schemas.Msg(code=200, msg='ok', data=data) + + +# 修改入职人员信息 +@router.post("/update_worker") +async def add_worker( + request: Request, + data_in: schemas.WorkerQuery, + db: AsyncIOMotorDatabase = Depends(get_database) +) -> schemas.Msg: + """ 修改入职人员信息 """ + + data = await crud.worker_info.update_worker(db, data_in.worker_query, data_in.data_in) + + return schemas.Msg(code=200, msg='ok', data=data) + + diff --git a/crud/__init__.py b/crud/__init__.py index 7eb64f8..8383b69 100644 --- a/crud/__init__.py +++ b/crud/__init__.py @@ -27,3 +27,4 @@ from .crud_interview_modes import api_interview_modes from .crud_email_record import email_record from .crud_operate_log import operate_log from .crud_interview_record import interview_record +from .crud_worker_info import worker_info diff --git a/crud/crud_worker_info.py b/crud/crud_worker_info.py new file mode 100644 index 0000000..cc77097 --- /dev/null +++ b/crud/crud_worker_info.py @@ -0,0 +1,34 @@ +from motor.motor_asyncio import AsyncIOMotorDatabase +import schemas +from crud.base import CRUDBase + +__all__ = 'worker_info', + + +class CRUDWorkerInfo(CRUDBase): + # 获取所有入职人员信息 + async def all_worker(self, db: AsyncIOMotorDatabase): + return await self.find_many(db, {}, {'_id': 0}) + + # 获取指定字段 + async def find_worker_some(self, db: AsyncIOMotorDatabase, findlist=[]): + findWhere = {'_id': 0} + if findlist: + for key in findlist: + findWhere.update({ + key: 1 + }) + return await self.find_many(db, {}, findWhere) + + # 更新一条入职人员信息 + async def update_worker(self, db: AsyncIOMotorDatabase, where, updatedata): + + await self.update_one(db, where, {'$set': updatedata}) + + # 插入一条新的面试数据数据 + async def insert_worker(self, db: AsyncIOMotorDatabase, insertdata): + + await self.insert_one(db, insertdata) + + +worker_info = CRUDWorkerInfo('worker_info') diff --git a/models/interview_zsgc.py b/models/interview_zsgc.py index 8513d36..edc31c0 100644 --- a/models/interview_zsgc.py +++ b/models/interview_zsgc.py @@ -412,9 +412,12 @@ 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}" + if whereStr: + sql += f" and toDate(addHours(`event_time`, 0)) >= '{start_time}'" + else: + sql += f" toDate(addHours(`event_time`, 0)) >= '{start_time}'" if end_time: - sql += f" and toDate(event_time) <= {end_time}" + sql += f" and toDate(addHours(`event_time`, 0)) <= '{end_time}'" # 没有日期条件 else: sql = f"select {findStr} from HR.resumes where {whereStr}" @@ -453,10 +456,15 @@ class InterviewDo: sql = f"select {findStr} from HR.resumes where {whereStr}" else: sql = f"select {findStr} from HR.resumes" + if start_time or end_time: + sql += f" where" if start_time: - sql += f" and toDate(event_time) >= {start_time}" + if whereStr: + sql += f" and toDate(addHours(`event_time`, 0)) >= '{start_time}'" + else: + sql += f" toDate(addHours(`event_time`, 0)) >= '{start_time}'" if end_time: - sql += f" and toDate(event_time) <= {end_time}" + sql += f" and toDate(addHours(`event_time`, 0)) <= '{end_time}'" # 没有日期条件 else: if whereStr: @@ -498,10 +506,15 @@ class InterviewDo: sql = f"select {findStr} from HR.resumes where {whereStr}" else: sql = f"select {findStr} from HR.resumes where '1'" + if start_time or end_time: + sql += f" where" if start_time: - sql += f" and toDate(event_time) >= {start_time}" + if whereStr: + sql += f" and toDate(addHours(`event_time`, 0)) >= '{start_time}'" + else: + sql += f" toDate(addHours(`event_time`, 0)) >= '{start_time}'" if end_time: - sql += f" and toDate(event_time) <= {end_time}" + sql += f" and toDate(addHours(`event_time`, 0)) <= '{end_time}'" # 没有日期条件 else: if whereStr: @@ -542,10 +555,15 @@ class InterviewDo: sql = f"select {findStr} from HR.resumes where {whereStr} and job_id in {job_ids}" else: sql = f"select {findStr} from HR.resumes where job_id in {job_ids}" + if start_time or end_time: + sql += f" where" if start_time: - sql += f" and toDate(event_time) >= {start_time}" + if whereStr: + sql += f" and toDate(addHours(`event_time`, 0)) >= '{start_time}'" + else: + sql += f" toDate(addHours(`event_time`, 0)) >= '{start_time}'" if end_time: - sql += f" and toDate(event_time) <= {end_time}" + sql += f" and toDate(addHours(`event_time`, 0)) <= '{end_time}'" # 没有日期条件 else: if whereStr: @@ -586,10 +604,15 @@ class InterviewDo: sql = f"select {findStr} from HR.resumes where {whereStr}" else: sql = f"select {findStr} from HR.resumes" + if start_time or end_time: + sql += f" where" if start_time: - sql += f" and toDate(event_time) >= {start_time}" + if whereStr: + sql += f" and toDate(addHours(`event_time`, 0)) >= '{start_time}'" + else: + sql += f" toDate(addHours(`event_time`, 0)) >= '{start_time}'" if end_time: - sql += f" and toDate(event_time) <= {end_time}" + sql += f" and toDate(addHours(`event_time`, 0)) <= '{end_time}'" # 没有日期条件 else: if whereStr: @@ -601,6 +624,52 @@ class InterviewDo: 'sql': sql } + # 招聘趋势分析sql + def get_trend_form_sql(self): + whereStr = '' + 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() + + # 有日期条件 + start_time = self.where.get('start_time', '') + end_time = self.where.get('end_time', '') + if is_date: + if whereStr: + sql = f"select toDate(addHours(`event_time`, 0)) as date,interview_stage, count() as value from HR.resumes where {whereStr}" + else: + sql = f"select toDate(addHours(`event_time`, 0)) as date,interview_stage, count() as value from HR.resumes" + if start_time or end_time: + sql += f" where" + if start_time: + if whereStr: + sql += f" and date >= '{start_time}'" + else: + sql += f" date >= '{start_time}'" + if end_time: + sql += f" and date <= '{end_time}'" + # 没有日期条件 + else: + if whereStr: + sql = f"select toDate(addHours(`event_time`, 0)) as date,interview_stage, count() as value from HR.resumes where {whereStr}" + else: + sql = f"select toDate(addHours(`event_time`, 0)) as date,interview_stage, count() as value from HR.resumes" + sql += f" group by date, interview_stage" + print(sql) + return { + 'sql': sql, + 'sdate': start_time, + 'edate': end_time, + } + if __name__ == '__main__': find = re.search(r"\d+\.?\d*", "18岁") diff --git a/schemas/__init__.py b/schemas/__init__.py index c25d890..3a6468c 100644 --- a/schemas/__init__.py +++ b/schemas/__init__.py @@ -33,3 +33,4 @@ from .email_record import * from .operate_log import * from .interview_modes import * from .interview_record import * +from .worker import * diff --git a/schemas/worker.py b/schemas/worker.py new file mode 100644 index 0000000..bfa9adc --- /dev/null +++ b/schemas/worker.py @@ -0,0 +1,14 @@ +from typing import Dict, List, Union, Any +from pydantic import BaseModel + + +# 面试数据包含字段样式 +class WorkerModel(BaseModel): + pass + + +# 面试查询格式 +class WorkerQuery(BaseModel): + data_in: Any + worker_query: dict = dict() # 查询删选条件 + find_filed: List[str] = None # 显示列名 diff --git a/utils/func.py b/utils/func.py index 24b172b..2bc6b2b 100644 --- a/utils/func.py +++ b/utils/func.py @@ -279,4 +279,18 @@ def start_end_month(time): this_month_start = datetime.datetime(now.year, now.month, 1) this_month_end = datetime.datetime(now.year, now.month, calendar.monthrange(now.year, now.month)[1]) this_month_end1 = this_month_end + timedelta(hours=23, minutes=59, seconds=59) - return this_month_start, this_month_end1 \ No newline at end of file + return this_month_start, this_month_end1 + + +# 获取两个日期之间的所有天数 +def get_every_days(sdate, edate): + days = [] + start_date = datetime.datetime.strptime(sdate, '%Y-%m-%d') + end_date = datetime.datetime.strptime(edate, '%Y-%m-%d') + different_day = end_date - start_date + day_num = different_day.days + for i in range(day_num + 1): + day = start_date + timedelta(days=i) + true_day = datetime.datetime.strftime(day, '%Y-%m-%d') + days.append(true_day) + return days diff --git a/接口文档/招聘趋势分析接口文档.txt b/接口文档/招聘趋势分析接口文档.txt new file mode 100644 index 0000000..f406bb1 --- /dev/null +++ b/接口文档/招聘趋势分析接口文档.txt @@ -0,0 +1,49 @@ +·: /api/v1/itr/interview_trend_form + +: +{ + "data_in": "string", Ϊַ "" + "interview_query": {"start_time":"2022-07-10", "end_time":"2022-07-15"}, ѯ ʼʱ "--"ʽַ + "find_column": [ Ϊб[] + "string" + ] +} + + +ֵ: +{ + "code": 200, + "msg": "ok", + "data": { ڶӦ + "2022-07-10": { + "ɸ": 0, + "ɸ": 0, + "Ե": 0 + }, + "2022-07-11": { + "ɸ": 0, + "ɸ": 0, + "Ե": 0 + }, + "2022-07-12": { + "ɸ": 2, + "ɸ": 2, + "Ե": 2 + }, + "2022-07-13": { + "ɸ": 0, + "ɸ": 0, + "Ե": 0 + }, + "2022-07-14": { + "ɸ": 0, + "ɸ": 0, + "Ե": 0 + }, + "2022-07-15": { + "ɸ": 0, + "ɸ": 0, + "Ե": 0 + } + } +} \ No newline at end of file