From b345c22932cdd85a9d4b07d58fec90d153fb79ec Mon Sep 17 00:00:00 2001 From: wuaho Date: Thu, 6 May 2021 13:04:58 +0800 Subject: [PATCH] 1 --- api/api_v1/api.py | 2 ++ api/api_v1/endpoints/dashboard.py | 60 +++++++++++++++++++++++++++++++ api/api_v1/endpoints/folder.py | 17 +++++++++ api/api_v1/endpoints/project.py | 7 ++-- api/api_v1/endpoints/report.py | 52 +++++++++++++++++++++++++++ api/api_v1/endpoints/space.py | 25 +++++++++++++ crud/__init__.py | 1 + crud/base.py | 16 ++++++--- crud/crud_folder.py | 2 +- crud/crud_project.py | 2 +- crud/crud_report.py | 30 ++++++++++++++++ crud/crud_space.py | 2 +- db/init_db.py | 4 +++ main.py | 2 +- schemas/__init__.py | 1 + schemas/dashboard.py | 22 ++++++++++++ schemas/folder.py | 4 +++ schemas/project.py | 4 +-- schemas/report.py | 36 +++++++++++++++++++ schemas/space.py | 4 +++ 20 files changed, 281 insertions(+), 12 deletions(-) create mode 100644 api/api_v1/endpoints/report.py create mode 100644 crud/crud_report.py create mode 100644 schemas/report.py diff --git a/api/api_v1/api.py b/api/api_v1/api.py index 535608c..6173cfe 100644 --- a/api/api_v1/api.py +++ b/api/api_v1/api.py @@ -4,6 +4,7 @@ from .endpoints import project from .endpoints import folder from .endpoints import space from .endpoints import dashboard +from .endpoints import report api_router = APIRouter() @@ -12,3 +13,4 @@ api_router.include_router(project.router, tags=["项目接口"], prefix='/projec api_router.include_router(folder.router, tags=["文件夹接口"], prefix='/folder') api_router.include_router(space.router, tags=["空间接口"], prefix='/space') api_router.include_router(dashboard.router, tags=["看板接口"], prefix='/dashboard') +api_router.include_router(report.router, tags=["报表接口"], prefix='/report') diff --git a/api/api_v1/endpoints/dashboard.py b/api/api_v1/endpoints/dashboard.py index 568ccfa..ed0f40f 100644 --- a/api/api_v1/endpoints/dashboard.py +++ b/api/api_v1/endpoints/dashboard.py @@ -22,3 +22,63 @@ async def create( # todo 建默认文件夹 return schemas.Msg(code=0, msg='ok', detail='创建成功') + + +@router.post("/delete") +async def delete( + data_in: schemas.DashboardDelete, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + # 删除Dashboard 自己创建的 + del_dashboard = await crud.dashboard.delete(db, _id=data_in.id, user_id=current_user.id) + + if del_dashboard.deleted_count == 0: + return schemas.Msg(code=-1, msg='error', detail='删除失败') + return schemas.Msg(code=0, msg='ok', detail='删除成功') + + +@router.post("/move") +async def delete( + data_in: schemas.DashboardMove, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + """ + 移动看板 + """ + res = await crud.dashboard.update_one(db, id=data_in.source_id, cat=data_in.cat, pid=data_in.dest_id) + if res.deleted_count == 0: + return schemas.Msg(code=-1, msg='error', detail='删除失败') + return schemas.Msg(code=0, msg='ok', detail='删除成功') + + +@router.post("/add_report") +async def add_report(data_in: schemas.AddReport, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) + ): + res = await crud.dashboard.update_one(db, id=data_in.id, **{'$push': {'reports': {'$each': data_in.report_ids}}}) + return schemas.Msg(code=0, msg='ok', detail='ok') + + +@router.post("/del_report") +async def add_report(data_in: schemas.DelReport, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) + ): + """删除报表""" + for item in data_in.report_ids: + await crud.dashboard.update_one(db, id=data_in.id, **{'$pull': {'reports': item}}) + return schemas.Msg(code=0, msg='ok', detail='ok') + + +@router.get("/") +async def add_report(_id: str, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) + ): + """获取一个看板""" + res = await crud.dashboard.get(db, id=_id) + res['reports'] = await crud.report.find_many(db, **{'$in': {'_id': res.get('reports')}}) + return schemas.Msg(code=0, msg='ok', detail=res['reports']) diff --git a/api/api_v1/endpoints/folder.py b/api/api_v1/endpoints/folder.py index 707524c..dc57968 100644 --- a/api/api_v1/endpoints/folder.py +++ b/api/api_v1/endpoints/folder.py @@ -15,6 +15,7 @@ async def create( db: AsyncIOMotorDatabase = Depends(get_database), current_user: schemas.UserDB = Depends(deps.get_current_user) ) -> schemas.Msg: + """创建文件夹""" try: await crud.folder.create(db, data_in, user_id=current_user.id) except pymongo.errors.DuplicateKeyError: @@ -22,3 +23,19 @@ async def create( # todo 建默认文件夹 return schemas.Msg(code=0, msg='ok', detail='创建成功') + + +@router.post("/delete") +async def delete( + data_in: schemas.FolderDelete, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + """删除文件夹""" + # 删除文件夹 自己创建的 + del_folder = await crud.folder.delete(db, _id=data_in.id, user_id=current_user.id) + # 删除文件夹下的 dashboard + del_dashboard = await crud.dashboard.delete(db, pid=data_in.id) + if del_folder.deleted_count == 0: + return schemas.Msg(code=-1, msg='error', detail='删除失败') + return schemas.Msg(code=0, msg='ok', detail='删除成功') diff --git a/api/api_v1/endpoints/project.py b/api/api_v1/endpoints/project.py index 7b629d9..609a6c2 100644 --- a/api/api_v1/endpoints/project.py +++ b/api/api_v1/endpoints/project.py @@ -16,6 +16,7 @@ async def create( db: AsyncIOMotorDatabase = Depends(get_database), current_user: schemas.UserDB = Depends(deps.get_current_user) ) -> schemas.Msg: + """创建项目""" try: await crud.project.create(db, data_in, user_id=current_user.id) except pymongo.errors.DuplicateKeyError: @@ -30,6 +31,7 @@ async def read_project( db: AsyncIOMotorDatabase = Depends(get_database), current_user: schemas.UserDB = Depends(deps.get_current_user) ): + """查看自己拥有的项目""" res = await crud.project.read_project(db, user_id=current_user.id) return res @@ -40,6 +42,7 @@ async def read_kanban( db: AsyncIOMotorDatabase = Depends(get_database), current_user: schemas.UserDB = Depends(deps.get_current_user) ): + """获取自己的看板""" res = {'kanban': [], 'space': []} # 我的看板 kanban = await crud.folder.read_folder(db, project_id=data_in.id, user_id=current_user.id, cat='kanban') @@ -80,14 +83,14 @@ async def read_kanban( 'dashboards': [], }) - for d in await crud.dashboard.find_many(db, pid=f['_id']): + for d in await crud.dashboard.find_many(db, pid=f['_id']): res['space'][-1]['folders'][-1]['dashboards'].append({ 'name': d['name'], '_id': d['_id'] }) # 空间 看板 - for d in await crud.dashboard.find_many(db, pid=item['_id']): + for d in await crud.dashboard.find_many(db, pid=item['_id']): res['space'][-1]['dashboards'].append({ 'name': d['name'], '_id': d['_id'] diff --git a/api/api_v1/endpoints/report.py b/api/api_v1/endpoints/report.py new file mode 100644 index 0000000..342a530 --- /dev/null +++ b/api/api_v1/endpoints/report.py @@ -0,0 +1,52 @@ +from typing import Any + +import pymongo +from fastapi import APIRouter, Depends +from motor.motor_asyncio import AsyncIOMotorDatabase +import crud, schemas + +from db import get_database +from api import deps + +router = APIRouter() + + +@router.post("/create") +async def create( + data_in: schemas.ReportCreate, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + """新建报表""" + try: + await crud.report.create(db, data_in, user_id=current_user.id) + except pymongo.errors.DuplicateKeyError: + return schemas.Msg(code=-1, msg='error', detail='报表已存在') + + return schemas.Msg(code=0, msg='ok', detail='创建成功') + + +@router.get("/") +async def read_report( + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> Any: + """获取已建报表""" + + res = await crud.report.read_report(db, user_id=current_user.id) + return res + + +@router.post("/delete") +async def delete( + data_in: schemas.ReportDelete, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + """删除报表""" + # 删除Report 自己创建的 + del_report = await crud.report.delete(db, _id=data_in.id, user_id=current_user.id) + + if del_report.deleted_count == 0: + return schemas.Msg(code=-1, msg='error', detail='删除失败') + return schemas.Msg(code=0, msg='ok', detail='删除成功') diff --git a/api/api_v1/endpoints/space.py b/api/api_v1/endpoints/space.py index 7bf707b..20e78be 100644 --- a/api/api_v1/endpoints/space.py +++ b/api/api_v1/endpoints/space.py @@ -15,6 +15,7 @@ async def create( db: AsyncIOMotorDatabase = Depends(get_database), current_user: schemas.UserDB = Depends(deps.get_current_user) ) -> schemas.Msg: + """创建空间""" try: await crud.space.create(db, data_in, user_id=current_user.id) except pymongo.errors.DuplicateKeyError: @@ -22,3 +23,27 @@ async def create( # todo 建默认文件夹 return schemas.Msg(code=0, msg='ok', detail='创建成功') + + +@router.post("/delete") +async def delete( + data_in: schemas.SpaceDelete, + db: AsyncIOMotorDatabase = Depends(get_database), + current_user: schemas.UserDB = Depends(deps.get_current_user) +) -> schemas.Msg: + """删除空间""" + # 删除空间 自己创建的 + del_space = await crud.space.delete(db, _id=data_in.id, user_id=current_user.id) + # 删除文件夹 + del_folder = await crud.folder.find_many(db, pid=data_in.id) + del_folder_ids = [f['_id'] for f in del_folder] + await crud.folder.delete(db, pid=data_in.id) + # 删除文件夹下的 dashboard + await crud.dashboard.delete(db, **{'$in': {'_id': del_folder_ids}}) + + # 删除空间下的 dashboard + await crud.dashboard.delete(db, pid=data_in.id) + + if del_space.deleted_count == 0: + return schemas.Msg(code=-1, msg='error', detail='删除失败') + return schemas.Msg(code=0, msg='ok', detail='删除成功') diff --git a/crud/__init__.py b/crud/__init__.py index 2a8c677..443e6d8 100644 --- a/crud/__init__.py +++ b/crud/__init__.py @@ -3,3 +3,4 @@ from .crud_project import project from .crud_folder import folder from .crud_space import space from .crud_dashboard import dashboard +from .crud_report import report diff --git a/crud/base.py b/crud/base.py index 28f8119..54fd77e 100644 --- a/crud/base.py +++ b/crud/base.py @@ -1,3 +1,5 @@ +from typing import Union + from bson import ObjectId @@ -5,15 +7,21 @@ class CRUDBase: def __init__(self, coll_name): self.coll_name = coll_name - async def get(self, coll, id: ObjectId): - return await coll.find_one({'_id': id}) + async def get(self, db, id: Union[ObjectId, str]): + return (await db[self.coll_name].find_one({'_id': ObjectId(id)})) or dict() - async def read_have(self, coll, user_id: str, **kwargs): + async def read_have(self, db, user_id: str, **kwargs): where = {'members': user_id} where.update(kwargs) - cursor = coll.find(where) + cursor = db[self.coll_name].find(where) return await cursor.to_list(length=999) async def find_many(self, db, **kwargs): cursor = db[self.coll_name].find(kwargs) return await cursor.to_list(length=999) + + async def delete(self, db, **kwargs): + return await db[self.coll_name].delete_many(kwargs) + + async def update_one(self, db, id, **kwargs): + return await db[self.coll_name].update_one({'_id': id}, kwargs) diff --git a/crud/crud_folder.py b/crud/crud_folder.py index f85a127..72b7c3f 100644 --- a/crud/crud_folder.py +++ b/crud/crud_folder.py @@ -19,7 +19,7 @@ class CRUDFolder(CRUDBase): await db[self.coll_name].insert_one(db_obj.dict(by_alias=True)) async def read_folder(self, db, user_id, project_id, cat): - return await self.read_have(db[self.coll_name], user_id=user_id, project_id=project_id, cat=cat) + return await self.read_have(db, user_id=user_id, project_id=project_id, cat=cat) async def create_index(self, db: AsyncIOMotorDatabase): await db[self.coll_name].create_index( diff --git a/crud/crud_project.py b/crud/crud_project.py index 349b10b..446190d 100644 --- a/crud/crud_project.py +++ b/crud/crud_project.py @@ -16,7 +16,7 @@ class CRUDProject(CRUDBase): await db[self.coll_name].insert_one(db_obj.dict(by_alias=True)) async def read_project(self, db: AsyncIOMotorDatabase, user_id: str): - return await self.read_have(db[self.coll_name], user_id=user_id) + return await self.read_have(db, user_id=user_id) async def create_index(self, db: AsyncIOMotorDatabase): await db[self.coll_name].create_index('name', unique=True) diff --git a/crud/crud_report.py b/crud/crud_report.py new file mode 100644 index 0000000..3eba83b --- /dev/null +++ b/crud/crud_report.py @@ -0,0 +1,30 @@ +import pymongo +from motor.motor_asyncio import AsyncIOMotorDatabase + +from crud.base import CRUDBase +from schemas import * + +__all__ = 'report', + + +class CRUDReport(CRUDBase): + + async def create(self, db: AsyncIOMotorDatabase, obj_in: ReportCreate, user_id: str): + db_obj = ReportDB( + **obj_in.dict(), user_id=user_id, + _id=uuid.uuid1().hex + + ) + await db[self.coll_name].insert_one(db_obj.dict(by_alias=True)) + + async def create_index(self, db: AsyncIOMotorDatabase): + await db[self.coll_name].create_index( + [('project_id', pymongo.DESCENDING), ('name', pymongo.DESCENDING), ('user_id', pymongo.DESCENDING)], + unique=True) + + async def read_report(self, db, user_id): + res = await self.read_have(db, user_id) + return res + + +report = CRUDReport('report') diff --git a/crud/crud_space.py b/crud/crud_space.py index 9d867e9..44b834e 100644 --- a/crud/crud_space.py +++ b/crud/crud_space.py @@ -19,7 +19,7 @@ class CRUDSpace(CRUDBase): await db[self.coll_name].insert_one(db_obj.dict(by_alias=True)) async def read_space(self, db, user_id, project_id): - return await self.read_have(db[self.coll_name], user_id=user_id, project_id=project_id) + return await self.read_have(db, user_id=user_id, project_id=project_id) async def create_index(self, db: AsyncIOMotorDatabase): await db[self.coll_name].create_index( diff --git a/db/init_db.py b/db/init_db.py index 9f34ee6..6c4a594 100644 --- a/db/init_db.py +++ b/db/init_db.py @@ -38,6 +38,9 @@ async def space_index(): async def dashboard_index(): await crud.dashboard.create_index(db) +async def report_index(): + await crud.report.create_index(db) + async def main(): await create_superuser() @@ -45,6 +48,7 @@ async def main(): await folder_index() await space_index() await dashboard_index() + await report_index() loop = asyncio.get_event_loop() diff --git a/main.py b/main.py index b5f50dd..e92e08d 100644 --- a/main.py +++ b/main.py @@ -24,4 +24,4 @@ from api.api_v1.api import api_router app.include_router(api_router, prefix=settings.API_V1_STR) if __name__ == '__main__': - uvicorn.run(app='main:app', host="127.0.0.1", port=8889, reload=True, debug=True) + uvicorn.run(app='main:app', host="0.0.0.0", port=8889, reload=True, debug=True) diff --git a/schemas/__init__.py b/schemas/__init__.py index c75a5b2..28e1487 100644 --- a/schemas/__init__.py +++ b/schemas/__init__.py @@ -4,3 +4,4 @@ from .project import * from .folder import * from .space import * from .dashboard import * +from .report import * \ No newline at end of file diff --git a/schemas/dashboard.py b/schemas/dashboard.py index 23ac70b..30d87dc 100644 --- a/schemas/dashboard.py +++ b/schemas/dashboard.py @@ -19,9 +19,30 @@ class DashboardCreate(DashboardBase): # cat: str pid: str + +class DashboardDelete(DBBase): + pass + + class Category(str, Enum): project = 'kanban' space = 'space' + + +class DashboardMove(BaseModel): + source_id: str + dest_id: str + cat: Category + + +class AddReport(DBBase): + report_ids: List[str] + + +class DelReport(DBBase): + report_ids: List[str] + + # -------------------------------------------------------------- # 数据库模型 class DashboardDB(DBBase): @@ -29,5 +50,6 @@ class DashboardDB(DBBase): user_id: str project_id: str # cat: Category + reports: List[str] = [] pid: str create_date: datetime = datetime.now() diff --git a/schemas/folder.py b/schemas/folder.py index 64ea33f..4868af6 100644 --- a/schemas/folder.py +++ b/schemas/folder.py @@ -20,6 +20,10 @@ class FolderCreate(FolderBase): pid: str +class FolderDelete(DBBase): + pass + + class Category(str, Enum): project = 'kanban' space = 'space' diff --git a/schemas/project.py b/schemas/project.py index 17fcc29..f38a144 100644 --- a/schemas/project.py +++ b/schemas/project.py @@ -2,7 +2,7 @@ import uuid from datetime import datetime from typing import List, Optional -from pydantic import BaseModel +from pydantic import BaseModel, Field from schemas import DBBase @@ -13,7 +13,7 @@ class ProjectBase(BaseModel): # 解析请求json 创建项目 class ProjectCreate(ProjectBase): - name: str + name: str = Field(..., title='项目名') # 查询某个项目看板 diff --git a/schemas/report.py b/schemas/report.py new file mode 100644 index 0000000..92d0b2e --- /dev/null +++ b/schemas/report.py @@ -0,0 +1,36 @@ +import json +import uuid +from datetime import datetime +from enum import Enum +from typing import List + +from pydantic import BaseModel, validator, Json + +from schemas import DBBase + + +class ReportBase(BaseModel): + name: str = None + query: str = None + project_id: str = None + + +class ReportCreate(ReportBase): + name: str + project_id: str + query: Json + + +class ReportDelete(DBBase): + pass + + +# -------------------------------------------------------------- +# 数据库模型 +class ReportDB(DBBase): + name: str + user_id: str + project_id: str + # cat: Category + pid: str + create_date: datetime = datetime.now() diff --git a/schemas/space.py b/schemas/space.py index dd53b16..6ce26f1 100644 --- a/schemas/space.py +++ b/schemas/space.py @@ -18,6 +18,10 @@ class SpaceCreate(SpaceBase): project_id: str +class SpaceDelete(DBBase): + pass + + # -------------------------------------------------------------- # 数据库模型 class SpaceDB(DBBase):