现有系统显示超级管理员的所有剧本的总数的统计:
希望优化为:
增加分组名和分组总数,以及所有的总数
且每个个数都支持点击,跳转到全部剧本列表中
先去要能从Django后端获取到
所有的Script的用户的组
每个组的组员,以及组员的剧本个数(已通过,未通过)
以及每个组的加起来的总数
现有的api接口:
return request(`${apiPrefix}/scripts/user_scripts_count/?${qsParamStr}`, {
后端代码是:
def user_scripts_count(self, request): """ 组下面的成员的 scripts 统计 全部用户的 scripts 统计 url 参数 function_group 传 id """ logger.info("user_scripts_count: request=%s", request) queryParams = request.query_params logger.info("queryParams=%s", queryParams) function_group_id = queryParams.get('function_group_id', None) logger.info("function_group_id=%s", function_group_id) if function_group_id: function_group = FunctionGroup.objects.get(pk=function_group_id) logger.info("function_group=%s", function_group) user_queryset = function_group.members.all() logger.info("user_queryset=%s", user_queryset) else: user_queryset = User.objects.filter(is_active=True) user_names = list(user_queryset.values_list('username', flat=True)) user_ids = list(user_queryset.values_list('id', flat=True)) all_user_scripts_count = [i.user_scripts.filter(Q(version=1)).count() for i in user_queryset] published_user_scripts_count = [i.user_scripts.filter(Q(publish_status='1')).count() for i in user_queryset] scriptSummaryInfoList = [] for index, value in enumerate(user_names): item = {} item['username'] = value item['user_id'] = user_ids[index] item['all_scripts_count'] = all_user_scripts_count[index] item['published_scripts_count'] = published_user_scripts_count[index] if int(published_user_scripts_count[index]) == 0: item['published_ratio'] = '0%' else: item['published_ratio'] = str(float('%.2f' % (100 * float(published_user_scripts_count[index])/float(all_user_scripts_count[index])))) + '%' scriptSummaryInfoList.append(item) logger.info("scriptSummaryInfoList=%s", scriptSummaryInfoList) scriptSummaryInfoListLen = len(scriptSummaryInfoList) logger.info("scriptSummaryInfoListLen=%s", scriptSummaryInfoListLen) return Response( { 'results': scriptSummaryInfoList, 'count': scriptSummaryInfoListLen }, status=status.HTTP_200_OK )
所返回的数据是:
[{ 'username': 'lxxx', 'user_id': UUID('1ed0a0d8-2abb-401d-9956-1493714200e3'), 'all_scripts_count': 7, 'published_scripts_count': 1, 'published_ratio': '14.29%' }, { 'username': 'xxx', 'user_id': UUID('44c3de7f-a0e4-4a62-8f64-720044ad6d03'), 'all_scripts_count': 101, 'published_scripts_count': 0, 'published_ratio': '0%' }, { 'username': 'irene', 'user_id': UUID('4c271812-d36e-4585-a1af-bd7ea6c6d449'), 'all_scripts_count': 24, 'published_scripts_count': 0, 'published_ratio': '0%' }, { 'username': 'xxx', 'user_id': UUID('4c875831-f6d4-446a-8acb-9cd2fac75ac5'), 'all_scripts_count': 102, 'published_scripts_count': 0, 'published_ratio': '0%' }, { 'username': 'Maggie', 'user_id': UUID('78c7b3c4-8a7c-4f1f-96ce-dc469fedad40'), 'all_scripts_count': 121, 'published_scripts_count': 121, 'published_ratio': '100.0%' }, { 'username': 'xxx', 'user_id': UUID('7dff71b1-e48f-4b94-ade3-c832742a8ed7'), 'all_scripts_count': 26, 'published_scripts_count': 0, 'published_ratio': '0%' }, { 'username': 'xxx', 'user_id': UUID('7e8832bc-c02d-4bef-a303-ed9488fb654a'), 'all_scripts_count': 1, 'published_scripts_count': 1, 'published_ratio': '100.0%' }, { 'username': 'victor', 'user_id': UUID('bb6d17b3-4fbd-431a-bcb1-0f134ca8509c'), 'all_scripts_count': 0, 'published_scripts_count': 0, 'published_ratio': '0%' }, { 'username': 'xxx', 'user_id': UUID('face7292-6b42-4858-a092-7b771512cba7'), 'all_scripts_count': 124, 'published_scripts_count': 123, 'published_ratio': '99.19%' }]
而此处,针对于全部的用户的列表,需要去调整返回数据的格式
希望前后端配置,显示这种的效果:
所以现在问题转化为:
【已解决】Antd Pro中如何动态自定义生成带合并单元格的表头
然后继续去优化自动生成Table的columns和data
以及优化从Django后台返回的数据的格式
期间:
【已解决】js中float浮点数转换为百分比后保留1位小数再转换为字符串
【总结】
此处已经可以通过:
前端reactjs:
import React, { PureComponent, Fragment } from 'react'; import { connect } from 'dva'; import { Card, Form, Table, } from 'antd'; import { routerRedux } from 'dva/router'; // import SimpleTable from 'components/SimpleTable'; import PageHeaderLayout from '../../layouts/PageHeaderLayout'; import styles from '../List/TableList.less'; import { isEmptyObj } from '../../utils/utils'; // @connect(({ script, loading }) => ({ @connect(({ script}) => ({ script, // loading: loading.models.script, })) @Form.create() export default class AllUserScriptCount extends PureComponent { state = { formValues: {}, currentPage: 1, pageSize: 20, }; componentDidMount() { console.log("AllUserScriptCount componentDidMount") this.props.dispatch({ type: 'script/fetchUserScriptsCount', }); } componentWillReceiveProps(nextProps){ console.log("AllUserScriptCount componentWillReceiveProps: nextProps=", nextProps) } handleStandardTableChange = (pagination) => { const { dispatch } = this.props; const { formValues } = this.state; this.setState({ currentPage: pagination.current, pageSize: pagination.pageSize, }); const params = { page: pagination.current, page_size: pagination.pageSize, ...formValues, }; dispatch({ type: 'script/fetchUserScriptsCount', payload: params, }); }; gotoAllScriptList = (searchPara) => { console.log("gotoAllScriptList: searchPara=", searchPara) const { dispatch } = this.props; dispatch(routerRedux.push({ pathname: '/script/all-script-list', // search: `?author_id=${authorId}`, search: searchPara, })); }; floatToPercentage = (value, record, index) => { console.log(`value=${value}, index=${index}`, "record=", record) // const percentValue = (value * 100).toFixed(1) const percentValue = (value * 100).toFixed(0) console.log("percentValue=", percentValue) return `${percentValue}%` } scriptListLink = (value, searchPara) => { console.log(`value=${value}, searchPara=${searchPara}`) return <a href="javascript:;" onClick={() => this.gotoAllScriptList(searchPara)} >{value}</a> } // merge `mergeRowCount` rows start from `mergeRowStartIdx` mergeRows = (value, record, index, searchPara) => { console.log(`value=${value}, index=${index}, searchPara=${searchPara}`, "record=", record) const obj = { // children: value, children: this.scriptListLink(value, searchPara), props: {}, } const mergeRowCount = record.viewInfo.mergeRowCount const mergeRowStartIdx = record.viewInfo.mergeRowStartIdx const endRowIdx = mergeRowStartIdx + (mergeRowCount - 1) if (mergeRowCount > 0) { if (index === mergeRowStartIdx) { obj.props.rowSpan = mergeRowCount } else if ((index >= (mergeRowStartIdx+1)) && (index <= endRowIdx)) { // other row are merged into first row cell obj.props.rowSpan = 0 } } console.log("obj=", obj) return obj } searchGroupOwner = (value, record, index) => { const searchPara = `?author_id=${record.groupOwner.userId}` return this.scriptListLink(value, searchPara) } searchMemberUser = (value, record, index) => { const searchPara = `?author_id=${record.memberInfo.userId}` console.log("searchMemberUser: searchPara=", searchPara) return this.scriptListLink(value, searchPara) } searchMemberUserPublished = (value, record, index) => { const searchPara = `?author_id=${record.memberInfo.userId}&publish_status=1` return this.scriptListLink(value, searchPara) } searchMemberUserUnpublished = (value, record, index) => { const searchPara = `?author_id=${record.memberInfo.userId}&publish_status=0` return this.scriptListLink(value, searchPara) } mergeRowsAndSearchGroup = (value, record, index) => { const searchPara = `?group_id=${record.groupId}` console.log("mergeRowsAndSearchGroup: searchPara=", searchPara) return this.mergeRows(value, record, index, searchPara) } mergeRowsAndSearchUser = (value, record, index, authorId) => { const searchPara = `?author_id=${authorId}` return this.mergeRows(value, record, index, searchPara) } mergeRowsAndSearchMemberUser = (value, record, index) => { return this.mergeRowsAndSearchUser(value, record, index, record.memberInfo.userId) } mergeRowsAndSearchGroupOwner = (value, record, index) => { return this.mergeRowsAndSearchUser(value, record, index, record.groupOwner.userId) } render() { const { script: { userScriptsCount }, loading } = this.props; const { currentPage, pageSize } = this.state; console.log("currentPage=", currentPage, ",pageSize=", pageSize) const columns = [ { title: '序号', // dataIndex: 'number', rowKey: 'number', render(value, record, index) { const number = (currentPage - 1) * pageSize return number + index + 1; }, }, { title: '剧本组', rowKey: 'scriptGroup', children: [ { title: '组名', dataIndex: 'groupName', rowKey: 'groupName', width: 140, render: this.mergeRowsAndSearchGroup, }, { title: '组长', dataIndex: 'groupOwner.username', rowKey: 'groupOwner.username', render: this.mergeRowsAndSearchGroupOwner, }, { title: '剧本总数', dataIndex: 'totalScriptCount', rowKey: 'totalScriptCount', width: 90, render: this.mergeRowsAndSearchGroup, }, ], }, { title: '组员', rowKey: 'memberInfo', dataIndex: 'memberInfo', children: [ { title: '用户名', dataIndex: 'memberInfo.username', rowKey: 'memberInfo.username', render: this.searchMemberUser, }, { title: '剧本总数', dataIndex: 'memberInfo.totalScriptCount', rowKey: 'memberInfo.totalScriptCount', render: this.searchMemberUser, }, { title: '已通过', dataIndex: 'memberInfo.publishedScriptCount', rowKey: 'memberInfo.publishedScriptCount', render: this.searchMemberUserPublished, }, { title: '未通过', dataIndex: 'memberInfo.unpublishedScriptCount', rowKey: 'memberInfo.unpublishedScriptCount', render: this.searchMemberUserUnpublished, }, { title: '通过率', dataIndex: 'memberInfo.publishedScriptRatio', rowKey: 'memberInfo.publishedScriptRatio', render: this.floatToPercentage, }, // { // title: '未通过率', // dataIndex: 'memberInfo.unpublishedScriptRatio', // rowKey: 'memberInfo.unpublishedScriptRatio', // render: this.floatToPercentage, // }, ], }, ] let data = [] // let data = [ // { // key: '1', // viewInfo: { // "mergeRowCount": 3, // "mergeRowStartIdx": 0, // }, // // number: 1, // groupId: "5", // groupName: 'Maggie剧本组', // groupOwner: { // "userId": "78c7b3c48a7c4f1f96cedc469fedad40", // "username": 'Maggie', // }, // totalScriptCount: 248, // memberInfo : { // 'username': 'Maggie', // 'userId': "78c7b3c48a7c4f1f96cedc469fedad40", // 'totalScriptCount': 121, // 'publishedScriptCount': 100, // "unpublishedScriptCount": 0, // 'publishedScriptRatio': 0.8264, // // 'unpublishedScriptRatio': 0.1735, // }, // }, // { // key: '2', // viewInfo: { // "mergeRowCount": 3, // "mergeRowStartIdx": 0, // }, // // number: 2, // groupId: "5", // groupName: 'Maggie剧本组', // groupOwner: { // "userId": "78c7b3c48a7c4f1f96cedc469fedad40", // "username": 'Maggie', // }, // totalScriptCount: 248, // memberInfo : { // 'username': 'xxx', // 'userId': "44c3de7fa0e44a628f64720044ad6d03", // 'totalScriptCount': 101, // 'publishedScriptCount': 101, // "unpublishedScriptCount": 0, // 'publishedScriptRatio': 1.00, // // 'unpublishedScriptRatio': 0.0, // }, // }, // { // key: '3', // viewInfo: { // "mergeRowCount": 3, // "mergeRowStartIdx": 0, // }, // // number: 3, // groupId: "5", // groupName: 'Maggie剧本组', // groupOwner: { // "userId": "78c7b3c48a7c4f1f96cedc469fedad40", // "username": 'Maggie', // }, // totalScriptCount: 248, // memberInfo : { // 'username': 'xxx', // 'userId': "7dff71b1e48f4b94ade3c832742a8ed7", // 'totalScriptCount': 26, // 'publishedScriptCount': 26, // "unpublishedScriptCount": 0, // 'publishedScriptRatio': 0.0, // // 'unpublishedScriptRatio': 1.00, // }, // }, // ]; console.log("userScriptsCount=", userScriptsCount) if (isEmptyObj(userScriptsCount)){ data = [] } else { data = userScriptsCount } console.log("table data=", data) return ( <PageHeaderLayout title="全部用户剧本统计"> <Card bordered={false}> <div className={styles.tableList}> {/* <SimpleTable loading={loading} data={userScriptsCount} columns={columns} /> */} <Table bordered columns={columns} dataSource={data} /> </div> </Card> </PageHeaderLayout> ); } }
后端Django:
@list_route( methods=['get'], url_path='user_scripts_count', url_name='user_scripts_count', permission_classes=[ IsAuthenticated, IsUserScriptFunctionGroup ]) def user_scripts_count(self, request): """ 组下面的成员的 scripts 统计 全部用户的 scripts 统计 url 参数 function_group 传 id """ logger.info("user_scripts_count: request=%s", request) queryParams = request.query_params logger.info("queryParams=%s", queryParams) function_group_id = queryParams.get('function_group_id', None) logger.info("function_group_id=%s", function_group_id) userScripSummaryList = [] functionGroupList = [] if function_group_id: # singleFunctionGroup = FunctionGroup.objects.get(pk=function_group_id) singleFunctionGroup = FunctionGroup.objects.get(pk=function_group_id, function=FunctionGroup.TYPE_VALUE_SCRIPT) logger.info("singleFunctionGroup=%s", singleFunctionGroup) # user_queryset = singleFunctionGroup.members.all() # logger.info("user_queryset=%s", user_queryset) functionGroupList.append(singleFunctionGroup) else: # user_queryset = User.objects.filter(is_active=True) allScriptGroup = FunctionGroup.objects.filter(function=FunctionGroup.TYPE_VALUE_SCRIPT) logger.info("allScriptGroup=%s", allScriptGroup) functionGroupList = allScriptGroup logger.info("functionGroupList=%s", functionGroupList) # # old code # user_names = list(user_queryset.values_list('username', flat=True)) # user_ids = list(user_queryset.values_list('id', flat=True)) # all_user_scripts_count = [i.user_scripts.filter(Q(version=1)).count() for i in user_queryset] # published_user_scripts_count = [i.user_scripts.filter(Q(publish_status='1')).count() for i in user_queryset] # scriptSummaryInfoList = [] # for index, value in enumerate(user_names): # item = {} # item['username'] = value # item['user_id'] = user_ids[index] # item['all_scripts_count'] = all_user_scripts_count[index] # item['published_scripts_count'] = published_user_scripts_count[index] # if int(published_user_scripts_count[index]) == 0: # item['published_ratio'] = '0%' # else: # item['published_ratio'] = str(float('%.2f' % (100 * float(published_user_scripts_count[index])/float(all_user_scripts_count[index])))) + '%' # scriptSummaryInfoList.append(item) # logger.info("scriptSummaryInfoList=%s", scriptSummaryInfoList) # scriptSummaryInfoListLen = len(scriptSummaryInfoList) # logger.info("scriptSummaryInfoListLen=%s", scriptSummaryInfoListLen) # userScripSummaryList = { # 'results': scriptSummaryInfoList, # 'count': scriptSummaryInfoListLen # } # allScriptGroupSerializer = FunctionGroupSerializer(allScriptGroup, many=True) # allScriptGroupSerializeData = allScriptGroupSerializer.data # logger.info("allScriptGroupSerializeData=%s", allScriptGroupSerializeData) # userScripSummaryList = allScriptGroupSerializeData allGroupAllScriptCount = 0 allGroupPublishedScriptCount = 0 allGroupUnpublishedScriptCount = 0 allGroupPublishedScriptRatio = 0.0 # allGroupUnpublishedScriptRatio = 0.0 curRowIdx = 0 for curGroupIdx, curFunctionGroup in enumerate(functionGroupList): logger.info("=== [%d] curFunctionGroup=%s", curGroupIdx, curFunctionGroup) curGroupAllMembers = curFunctionGroup.members.all() logger.info("curGroupAllMembers=%s", curGroupAllMembers) groupMemerCount = len(curGroupAllMembers) logger.info("groupMemerCount=%s", groupMemerCount) groupOwner = curFunctionGroup.owner logger.info("groupOwner=%s", groupOwner) # update group total script count curGroupTotalScriptCount = 0 for eachMember in curGroupAllMembers: curUserTotalScriptCount = eachMember.user_scripts.filter(Q(version=1)).count() curGroupTotalScriptCount += curUserTotalScriptCount logger.info("curGroupTotalScriptCount=%s", curGroupTotalScriptCount) allGroupAllScriptCount += curGroupTotalScriptCount curGroupDict = { "viewInfo": { "mergeRowCount": groupMemerCount, "mergeRowStartIdx": curRowIdx, }, "groupId": curFunctionGroup.id, "groupName": curFunctionGroup.name, "groupOwner": { "userId": groupOwner.id, "username": groupOwner.username, }, "totalScriptCount": curGroupTotalScriptCount, "memberInfo" : {} } logger.info("curGroupDict=%s", curGroupDict) for curMemberIdx, curMember in enumerate(curGroupAllMembers): logger.info("curMemberIdx=%s,curMember=%s", curMemberIdx, curMember) curUserAllScripts = curMember.user_scripts logger.info("curUserAllScripts=%s", curUserAllScripts) totalScriptCount = curUserAllScripts.filter(Q(version=1)).count() publishedScriptCount = curUserAllScripts.filter(Q(publish_status=Script.PUBLISH_STATUS_PUBLISHED)).count() unpublishedScriptCount = curUserAllScripts.filter(Q(publish_status=Script.PUBLISH_STATUS_UNPUBLISHED)).count() allGroupPublishedScriptCount += publishedScriptCount allGroupUnpublishedScriptCount += unpublishedScriptCount if (totalScriptCount > 0): publishedScriptRatio = float(publishedScriptCount)/float(totalScriptCount) # unpublishedScriptRatio = float(unpublishedScriptCount)/float(totalScriptCount) else: publishedScriptRatio = 0.0 # unpublishedScriptRatio = 0.0 curUserDict = copy.deepcopy(curGroupDict) curUserDict["memberInfo"] = { 'username': curMember.username, 'userId': curMember.id, 'totalScriptCount': totalScriptCount, 'publishedScriptCount': publishedScriptCount, "unpublishedScriptCount": unpublishedScriptCount, 'publishedScriptRatio': publishedScriptRatio, # 'unpublishedScriptRatio': unpublishedScriptRatio, } curUserDict["key"] = curRowIdx logger.info("--- [%d] curUserDict=%s", curRowIdx, curUserDict) userScripSummaryList.append(curUserDict) curRowIdx += 1 # generate last total summary item if (allGroupAllScriptCount > 0): allGroupPublishedScriptRatio = float(allGroupPublishedScriptCount)/float(allGroupAllScriptCount) # allGroupUnpublishedScriptRatio = float(allGroupUnpublishedScriptCount)/float(allGroupAllScriptCount) else: allGroupPublishedScriptRatio = 0.0 # allGroupUnpublishedScriptRatio = 0.0 lastTotalDict = { "viewInfo": { "mergeRowCount": 0, "mergeRowStartIdx": 0, }, "groupId": "", "groupName": "总计", "groupOwner": { "userId": "", "username": "", }, "totalScriptCount": allGroupAllScriptCount, "memberInfo" : { 'username': "", 'userId': "", 'totalScriptCount': allGroupAllScriptCount, 'publishedScriptCount': allGroupPublishedScriptCount, "unpublishedScriptCount": allGroupUnpublishedScriptCount, 'publishedScriptRatio': allGroupPublishedScriptRatio, # 'unpublishedScriptRatio': allGroupUnpublishedScriptRatio, } } userScripSummaryList.append(lastTotalDict) logger.info("userScripSummaryList=%s", userScripSummaryList) userScripSummaryCount = len(userScripSummaryList) logger.info("userScripSummaryCount=%s", userScripSummaryCount) return Response(userScripSummaryList, status=status.HTTP_200_OK)
实现了全部剧本统计的页面:
且点击对应的内容,可以跳转到全部剧本列表页且传递对应参数:
【后记 180803】
折腾:
【已解决】Django出错:TypeError: object of type ‘ManyRelatedManager’ has no len()
后,更加了解了,many to many的manager,直接打印的值是None
但是可以用queryset去获取自己需要的值的
回头再去看上面的代码:
curUserAllScripts = curMember.user_scripts logger.info("curUserAllScripts=%s", curUserAllScripts) totalScriptCount = curUserAllScripts.filter(Q(version=1)).count() publishedScriptCount = curUserAllScripts.filter(Q(publish_status=Script.PUBLISH_STATUS_PUBLISHED)).count() unpublishedScriptCount = curUserAllScripts.filter(Q(publish_status=Script.PUBLISH_STATUS_UNPUBLISHED)).count()
通过调试发现:
RelatedManager
的问题
且log中都是:
curUserAllScripts=script.Script.None
之类的None的值,而不是我们希望看到的值
才更加明白,其实curUserAllScripts也是个manager
其中定义是:
class Script(TimestampedModel): author = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name='user_scripts', null=False, blank=False)
通过打印type:
type(curUserAllScripts)=<class 'django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager'>
是属于many to one的类型
所以应该去改名:
curUserAllScriptsRelatedManager = curMember.user_scripts logger.info("type(curUserAllScriptsRelatedManager)=%s", type(curUserAllScriptsRelatedManager)) logger.info("curUserAllScriptsRelatedManager=%s", curUserAllScriptsRelatedManager) totalScriptCount = curUserAllScriptsRelatedManager.filter(Q(version=1)).count() publishedScriptCount = curUserAllScriptsRelatedManager.filter(Q(publish_status=Script.PUBLISH_STATUS_PUBLISHED)).count() unpublishedScriptCount = curUserAllScriptsRelatedManager.filter(Q(publish_status=Script.PUBLISH_STATUS_UNPUBLISHED)).count()
转载请注明:在路上 » 【已解决】剧本编写系统中优化超级管理员的全部剧本统计页