This commit is contained in:
wuaho 2021-05-06 13:04:58 +08:00
parent c06629c9a2
commit b345c22932
20 changed files with 281 additions and 12 deletions

View File

@ -4,6 +4,7 @@ from .endpoints import project
from .endpoints import folder from .endpoints import folder
from .endpoints import space from .endpoints import space
from .endpoints import dashboard from .endpoints import dashboard
from .endpoints import report
api_router = APIRouter() 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(folder.router, tags=["文件夹接口"], prefix='/folder')
api_router.include_router(space.router, tags=["空间接口"], prefix='/space') api_router.include_router(space.router, tags=["空间接口"], prefix='/space')
api_router.include_router(dashboard.router, tags=["看板接口"], prefix='/dashboard') api_router.include_router(dashboard.router, tags=["看板接口"], prefix='/dashboard')
api_router.include_router(report.router, tags=["报表接口"], prefix='/report')

View File

@ -22,3 +22,63 @@ async def create(
# todo 建默认文件夹 # todo 建默认文件夹
return schemas.Msg(code=0, msg='ok', detail='创建成功') 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'])

View File

@ -15,6 +15,7 @@ async def create(
db: AsyncIOMotorDatabase = Depends(get_database), db: AsyncIOMotorDatabase = Depends(get_database),
current_user: schemas.UserDB = Depends(deps.get_current_user) current_user: schemas.UserDB = Depends(deps.get_current_user)
) -> schemas.Msg: ) -> schemas.Msg:
"""创建文件夹"""
try: try:
await crud.folder.create(db, data_in, user_id=current_user.id) await crud.folder.create(db, data_in, user_id=current_user.id)
except pymongo.errors.DuplicateKeyError: except pymongo.errors.DuplicateKeyError:
@ -22,3 +23,19 @@ async def create(
# todo 建默认文件夹 # todo 建默认文件夹
return schemas.Msg(code=0, msg='ok', detail='创建成功') 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='删除成功')

View File

@ -16,6 +16,7 @@ async def create(
db: AsyncIOMotorDatabase = Depends(get_database), db: AsyncIOMotorDatabase = Depends(get_database),
current_user: schemas.UserDB = Depends(deps.get_current_user) current_user: schemas.UserDB = Depends(deps.get_current_user)
) -> schemas.Msg: ) -> schemas.Msg:
"""创建项目"""
try: try:
await crud.project.create(db, data_in, user_id=current_user.id) await crud.project.create(db, data_in, user_id=current_user.id)
except pymongo.errors.DuplicateKeyError: except pymongo.errors.DuplicateKeyError:
@ -30,6 +31,7 @@ async def read_project(
db: AsyncIOMotorDatabase = Depends(get_database), db: AsyncIOMotorDatabase = Depends(get_database),
current_user: schemas.UserDB = Depends(deps.get_current_user) current_user: schemas.UserDB = Depends(deps.get_current_user)
): ):
"""查看自己拥有的项目"""
res = await crud.project.read_project(db, user_id=current_user.id) res = await crud.project.read_project(db, user_id=current_user.id)
return res return res
@ -40,6 +42,7 @@ async def read_kanban(
db: AsyncIOMotorDatabase = Depends(get_database), db: AsyncIOMotorDatabase = Depends(get_database),
current_user: schemas.UserDB = Depends(deps.get_current_user) current_user: schemas.UserDB = Depends(deps.get_current_user)
): ):
"""获取自己的看板"""
res = {'kanban': [], 'space': []} res = {'kanban': [], 'space': []}
# 我的看板 # 我的看板
kanban = await crud.folder.read_folder(db, project_id=data_in.id, user_id=current_user.id, cat='kanban') 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': [], '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({ res['space'][-1]['folders'][-1]['dashboards'].append({
'name': d['name'], 'name': d['name'],
'_id': d['_id'] '_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({ res['space'][-1]['dashboards'].append({
'name': d['name'], 'name': d['name'],
'_id': d['_id'] '_id': d['_id']

View File

@ -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='删除成功')

View File

@ -15,6 +15,7 @@ async def create(
db: AsyncIOMotorDatabase = Depends(get_database), db: AsyncIOMotorDatabase = Depends(get_database),
current_user: schemas.UserDB = Depends(deps.get_current_user) current_user: schemas.UserDB = Depends(deps.get_current_user)
) -> schemas.Msg: ) -> schemas.Msg:
"""创建空间"""
try: try:
await crud.space.create(db, data_in, user_id=current_user.id) await crud.space.create(db, data_in, user_id=current_user.id)
except pymongo.errors.DuplicateKeyError: except pymongo.errors.DuplicateKeyError:
@ -22,3 +23,27 @@ async def create(
# todo 建默认文件夹 # todo 建默认文件夹
return schemas.Msg(code=0, msg='ok', detail='创建成功') 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='删除成功')

View File

@ -3,3 +3,4 @@ from .crud_project import project
from .crud_folder import folder from .crud_folder import folder
from .crud_space import space from .crud_space import space
from .crud_dashboard import dashboard from .crud_dashboard import dashboard
from .crud_report import report

View File

@ -1,3 +1,5 @@
from typing import Union
from bson import ObjectId from bson import ObjectId
@ -5,15 +7,21 @@ class CRUDBase:
def __init__(self, coll_name): def __init__(self, coll_name):
self.coll_name = coll_name self.coll_name = coll_name
async def get(self, coll, id: ObjectId): async def get(self, db, id: Union[ObjectId, str]):
return await coll.find_one({'_id': id}) 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 = {'members': user_id}
where.update(kwargs) where.update(kwargs)
cursor = coll.find(where) cursor = db[self.coll_name].find(where)
return await cursor.to_list(length=999) return await cursor.to_list(length=999)
async def find_many(self, db, **kwargs): async def find_many(self, db, **kwargs):
cursor = db[self.coll_name].find(kwargs) cursor = db[self.coll_name].find(kwargs)
return await cursor.to_list(length=999) 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)

View File

@ -19,7 +19,7 @@ class CRUDFolder(CRUDBase):
await db[self.coll_name].insert_one(db_obj.dict(by_alias=True)) await db[self.coll_name].insert_one(db_obj.dict(by_alias=True))
async def read_folder(self, db, user_id, project_id, cat): 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): async def create_index(self, db: AsyncIOMotorDatabase):
await db[self.coll_name].create_index( await db[self.coll_name].create_index(

View File

@ -16,7 +16,7 @@ class CRUDProject(CRUDBase):
await db[self.coll_name].insert_one(db_obj.dict(by_alias=True)) await db[self.coll_name].insert_one(db_obj.dict(by_alias=True))
async def read_project(self, db: AsyncIOMotorDatabase, user_id: str): 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): async def create_index(self, db: AsyncIOMotorDatabase):
await db[self.coll_name].create_index('name', unique=True) await db[self.coll_name].create_index('name', unique=True)

30
crud/crud_report.py Normal file
View File

@ -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')

View File

@ -19,7 +19,7 @@ class CRUDSpace(CRUDBase):
await db[self.coll_name].insert_one(db_obj.dict(by_alias=True)) await db[self.coll_name].insert_one(db_obj.dict(by_alias=True))
async def read_space(self, db, user_id, project_id): 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): async def create_index(self, db: AsyncIOMotorDatabase):
await db[self.coll_name].create_index( await db[self.coll_name].create_index(

View File

@ -38,6 +38,9 @@ async def space_index():
async def dashboard_index(): async def dashboard_index():
await crud.dashboard.create_index(db) await crud.dashboard.create_index(db)
async def report_index():
await crud.report.create_index(db)
async def main(): async def main():
await create_superuser() await create_superuser()
@ -45,6 +48,7 @@ async def main():
await folder_index() await folder_index()
await space_index() await space_index()
await dashboard_index() await dashboard_index()
await report_index()
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()

View File

@ -24,4 +24,4 @@ from api.api_v1.api import api_router
app.include_router(api_router, prefix=settings.API_V1_STR) app.include_router(api_router, prefix=settings.API_V1_STR)
if __name__ == '__main__': 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)

View File

@ -4,3 +4,4 @@ from .project import *
from .folder import * from .folder import *
from .space import * from .space import *
from .dashboard import * from .dashboard import *
from .report import *

View File

@ -19,9 +19,30 @@ class DashboardCreate(DashboardBase):
# cat: str # cat: str
pid: str pid: str
class DashboardDelete(DBBase):
pass
class Category(str, Enum): class Category(str, Enum):
project = 'kanban' project = 'kanban'
space = 'space' 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): class DashboardDB(DBBase):
@ -29,5 +50,6 @@ class DashboardDB(DBBase):
user_id: str user_id: str
project_id: str project_id: str
# cat: Category # cat: Category
reports: List[str] = []
pid: str pid: str
create_date: datetime = datetime.now() create_date: datetime = datetime.now()

View File

@ -20,6 +20,10 @@ class FolderCreate(FolderBase):
pid: str pid: str
class FolderDelete(DBBase):
pass
class Category(str, Enum): class Category(str, Enum):
project = 'kanban' project = 'kanban'
space = 'space' space = 'space'

View File

@ -2,7 +2,7 @@ import uuid
from datetime import datetime from datetime import datetime
from typing import List, Optional from typing import List, Optional
from pydantic import BaseModel from pydantic import BaseModel, Field
from schemas import DBBase from schemas import DBBase
@ -13,7 +13,7 @@ class ProjectBase(BaseModel):
# 解析请求json 创建项目 # 解析请求json 创建项目
class ProjectCreate(ProjectBase): class ProjectCreate(ProjectBase):
name: str name: str = Field(..., title='项目名')
# 查询某个项目看板 # 查询某个项目看板

36
schemas/report.py Normal file
View File

@ -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()

View File

@ -18,6 +18,10 @@ class SpaceCreate(SpaceBase):
project_id: str project_id: str
class SpaceDelete(DBBase):
pass
# -------------------------------------------------------------- # --------------------------------------------------------------
# 数据库模型 # 数据库模型
class SpaceDB(DBBase): class SpaceDB(DBBase):