import React from 'react';
import ReactDom from 'react-dom';
-import {BrowserRouter as Router, Route, Link, Redirect, Switch} from 'react-router-dom';
+import {HashRouter as Router, Route, Link, Redirect, Switch} from 'react-router-dom';
import createHistory from 'history/createHashHistory'
const history = createHistory()
import {spring, AnimatedRoute, AnimatedSwitch} from 'react-router-transition';
import Status from './page/status/index.jsx'
import PPolicy from './page/ppolicy/index.jsx'
import DetailInfo from './page/detailInfo/index.jsx'
+import MachinelInfo from './page/machineInfo/index.jsx'
import Portal from './page/portal/index.jsx'
// we need to map the `scale` prop we define below
<Route exact path="/portal/" component={Portal}/>
<Route path="/detailInfo/:uuid" component={DetailInfo}/>
- {/*<Route path="/detail/:uuid" component={DetailInfo}/>*/}
+ <Route path="/machineInfo/:machine_sn" component={MachinelInfo}/>
{/*<Redirect exact from="/order" to="/order/index"/>*/}
{/*<Redirect exact from="/user" to="/user/index"/>*/}
{/*<Route component={ErrorPage}/>*/}
--- /dev/null
+import React from 'react';
+
+import NavTop from 'component/nav-top/index.jsx';
+// import './index.css';
+import {Image, Card, Button, List, Icon} from 'semantic-ui-react'
+class FarmerDetailCard extends React.Component {
+ constructor(props){
+ super(props);
+ }
+ render(){
+ let machine = this.props.machine || {}
+ let system = machine.os_name + ' ' + machine.os_version;
+ let camp = machine.comp_name + ' ' + machine.comp_version;
+ return (
+
+ <div className="farmer-card">
+ <Card>
+ <Card.Content>
+ <Image floated='right' size='mini'
+ src={machine.avatar}/>
+ <Card.Header>Farmer: {machine.alias}</Card.Header>
+ <Card.Meta>report num: {machine.reports}</Card.Meta>
+ <Card.Description>
+ <List>
+ <List.Item icon='computer' content={system} />
+ <List.Item icon='microchip' content={camp} />
+ <List.Item
+ icon='mail'
+ content={<a href={machine.owner}>{machine.owner}</a>}
+ />
+ </List>
+ </Card.Description>
+ </Card.Content>
+ {/*<Card.Content extra>*/}
+ {/*<div className='ui buttons'>*/}
+ {/*/!*todo link to machine page*!/*/}
+ {/*<Button basic color='blue'>*/}
+ {/*Other records*/}
+ {/*</Button>*/}
+ {/*</div>*/}
+ {/*</Card.Content>*/}
+ </Card>
+ </div>
+ );
+ }
+}export default FarmerDetailCard;
+
constructor(props) {
super(props);
this.state = {
- username: _util.getStorage('userInfo').username || '',
+ // username: _util.getStorage('userInfo').username || '',
+ userinfo: _util.getStorage('userInfo')|| {},
isLoggedIn: true
}
}
window.location.href = '/login';
}
+ componentWillMount(){
+ this.setState({
+ isLoggedIn: this.isLoggedIn(),
+ });
+ }
+
+ isLoggedIn() {
+ if(this.state.userinfo.token){
+ return true;
+ }
+ return false;
+ }
+
render() {
- const isLoggedIn = this.state.isLoggedIn;
+ let isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
</li>;
} else {
button = <li className="dropdown sign-in">
- <a className="" href="/login" aria-expanded="false">
+ <Link to="/login" aria-expanded="false">
Sign in
- </a>
+ </Link>
</li>;
}
import React from 'react';
// import './index.css';
import PGUtil from 'util/util.jsx'
+import PGConstant from 'util/constant.jsx'
const _util = new PGUtil();
import User from 'service/user-service.jsx'
const _user = new User();
componentWillMount() {
document.title = 'LOGIN';
+ let userinfo = _util.getStorage('userInfo')|| {};
+ if(userinfo.token){
+ this.props.history.push('/portal')
+ // window.location.href = '/portal';
+ }
+
}
onInputChange(e) {
// check success
if (checkResult.status) {
_user.login(loginInfo).then((res) => {
- console.dir(res)
+ // console.dir(res)
_util.setStorage('userInfo', res);
- this.props.history.push(this.state.redirect);
- }, (errMsg) => {
- // console.log(errMsg)
- _util.errorTips(errMsg);
+ // this.props.history.push(this.state.redirect);
+ window.location.href = this.state.redirect;
+ }, (err) => {
+ // console.log(err)
+ if (PGConstant.AuthorizedErrorCode === err) {
+ _util.errorTips('username or password is mistake!');
+ }else{
+ _util.errorTips('login fail');
+ }
});
}
// check failure
else {
+
_util.errorTips(checkResult.msg);
}
--- /dev/null
+.machine-info-divier-div{
+ margin-left: 15px!important;
+ float: left;
+}
+.machine-info-divier{
+ float: left;
+}
+.blank{
+ float: left;
+ width: 15px;
+ height: 2px;
+}
\ No newline at end of file
import React from 'react';
// import './index.css';
-import ResultFilter from 'component/result-filter/index.jsx';
-import BasicTable from 'util/basic-table/index.jsx';
+import {Table, Divider, Segment, Icon} from 'semantic-ui-react'
+import FarmerDetailCard from 'component/farmer-detail-card/index.jsx'
import Record from 'service/record-service.jsx'
import PGUtil from 'util/util.jsx'
-
+import MachineRecordTable from 'util/machine-record-table/index.jsx'
const _util = new PGUtil();
const _record = new Record();
class MachineInfo extends React.Component {
constructor(props) {
super(props);
this.state = {
+ machineNo: this.props.match.params.machine_sn,
+ machineInfo: {},
isLoading: false,
currentPage: 1,
total:3,
filter: {},
list: [
]
-
},
-
+ console.dir(this.props.match.params)
this.onPageChange = this.onPageChange.bind(this);
- this.onIsLoadingChange = this.onIsLoadingChange.bind(this);
this.handleApplyBtnClick = this.handleApplyBtnClick.bind(this);
- this.loadRecordList = this.loadRecordList.bind(this);
+ this.loadHistoryRecordList = this.loadHistoryRecordList.bind(this);
}
componentDidMount() {
}
// load history record list
- loadHistoryRecordList(page=1) {
+ loadHistoryRecordList() {
let _this = this;
let listParam = {};
- listParam= this.state.filter;
- listParam.page = page;
-
- _record.getRecordList(listParam).then(res => {
+ // listParam= this.state.filter;
+ // listParam.page = page;
+ listParam.machine_sn = this.state.machineNo;
+ _record.getHistoryRecordList(listParam).then(res => {
console.log('res is:' + res)
this.setState({
- list: res.results,
- total: res.count,
+ machineInfo: res.machine_info || {},
+ list: res.reports || [],
+ // total: res.count,
isLoading: false
});
- _this.changeIsLoading(false);
+ // _this.changeIsLoading(false);
}, errMsg => {
this.setState({
list: []
};
return (
- <div id="page-wrapper">
- <BasicTable list={this.state.list} total={this.state.total} current={this.state.currentPage} loadfunc={this.loadRecordList}/>
+ <div className="container-fluid detail-container">
+ <div className="record-title">
+
+ <div className="record-title-top">
+ <span>NO: {this.state.machineNo}</span>
+ {/*<span>Add Date: {this.state.machineInfo.addtime}</span>*/}
+ </div>
+ <h2 >Farmer: {this.state.machineInfo.alias}</h2>
+ </div>
+
+ <div className="machine-info-divier-div">
+ <div className="blank"></div>
+ <Divider className="machine-info-divier"></Divider>
+ </div>
+ <div className="col-md-3">
+ <Segment vertical>Farmer Info</Segment>
+ <FarmerDetailCard machine={this.state.machineInfo}></FarmerDetailCard>
+ </div>
+
+ <div className="col-md-9">
+ {/*<div className="card-container row">*/}
+
+ <div className="info-container col-md-12 col-md-offset-1">
+ <MachineRecordTable list={this.state.list} total={this.state.total} current={this.state.currentPage} loadfunc={this.loadRecordList}/>
+ </div>
+
+ </div>
</div>
)
}
import React from 'react';
import './index.css';
+import { hashHistory } from 'react-router'
import ResultFilter from 'component/result-filter/index.jsx';
import MachineTable from 'util/machine-table/index.jsx';
import UserInfoCard from 'component/userinfo-card/index.jsx'
}
componentDidMount(){
+
let user = _util.getStorage('userInfo')
this.setState({
username: user.username,
}
onLogout(){
_util.removeStorage('userInfo');
- window.location.href = '/login';
+ // this.props.history.push('/login')
+ // hashHistory.push('/login')
+ window.location.href = '/';
// _user.logout().then(res => {
// _util.removeStorage('userInfo');
// window.location.href = '/login';
// data : {'Ldw7RrdP7jj4q89kgXCfeY'}
});
}
+
+ // get history record list
+ getHistoryRecordList(listParam){
+ let url = PGConstant.base_url + '/machine-records/' + listParam.machine_sn;
+
+ let data = {};
+ // data = listParam;
+
+ console.log('final data')
+ console.dir(listParam);
+ return _util.request({
+ type : 'get',
+ url : url,
+ data : data
+ });
+ }
}
export default Record;
\ No newline at end of file
name:'好123',
UnauthorizedCode:401,// unlogin
+ AuthorizedErrorCode:400,// username or pwd mistake
};
export default object;
\ No newline at end of file
--- /dev/null
+.ReactTable .rt-th{
+ background-color: #ffffff;
+}
+
+.ReactTable .-pagination{
+ background-color: #ffffff;
+}
+
+.ReactTable .rt-tr-group:nth-child(2n) {
+ background-color: #ffffff;
+}
+
+.bgc-clear{
+ background-color: #ffffff;
+}
+
+.anonymous {
+ color: #e8e8e8;
+}
+
+.anonymous :hover {
+ color: lightgrey;
+}
+.improved {
+ color: #7cb305;
+}
+
+.quo {
+ color: #40a9ff;
+}
+
+.regressive {
+ color: #fa541c;
+}
+
+.mini-label {
+ font-weight: 100!important;
+ font-size: 0.1em!important;
+ min-width: 1.3em!important;
+ min-height: 0.1em!important;
+ padding: .2em!important;
+ line-height: 0.5em!important;
+}
\ No newline at end of file
--- /dev/null
+import React from 'react';
+import {Link} from 'react-router-dom';
+import {Icon, Table, Label, Message, Button} from 'semantic-ui-react'
+import Pagination from 'util/pagination/index.jsx'
+import './index.css';
+
+function Bubble(props) {
+
+ if (props.num <= 0) {
+ return null;
+ }
+ let className = props.name + 'IconClassName';
+ return (
+ <Label circular size="mini" className={"mini-label " + className}>
+ {props.num}
+ </Label>
+ );
+}
+
+//todo
+// function TrendCell(trend) {
+// const isNull = !list;
+// const isEmpty = !isNull && !list.length;
+// let improvedIconClassName = trend.improved > 0 ? 'improved' : 'anonymous'
+// let quoIconClassName = trend.quo > 0 ? 'quo' : 'anonymous'
+// let regressiveIconClassName = trend.regressive > 0 ? 'regressive' : 'anonymous'
+// if (!trend.is_first) {
+// return (
+// <Table.Cell textAlign='center'>
+// first report
+// </Table.Cell>
+// );
+// } else {
+// return (
+// <div>
+// <Table.Cell textAlign='center'>
+// <Icon className={"bgc-clear " + improvedIconClassName} name='smile outline' size='large'/>
+// <Bubble num={trend.improved} name="improved"/>
+// </Table.Cell>
+// <Table.Cell textAlign='center'>
+// <Icon className={"bgc-clear " + quoIconClassName} name='meh outline' size='large'/>
+// <Bubble num={trend.quo} name="quo"/>
+// </Table.Cell>
+// <Table.Cell textAlign='center'>
+// <Icon className={"bgc-clear " + regressiveIconClassName} name='frown outline'
+// size='large'/>
+// <Bubble num={trend.regressive} name="regressive"/>
+// </Table.Cell>
+// </div>
+// );
+// }
+//
+// }
+
+// general basic table
+class MachineRecordTable extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ isFirstLoading: true,
+ total: this.props.total,
+ currentPage: 1,
+ }
+ }
+
+
+ // onPageNumChange(current) {
+ // this.setState({
+ // currentPage: current
+ // }, () => {
+ // this.props.loadfunc(current);
+ // });
+ // console.log('current:' + this.state.currentPage)
+ // }
+
+ render() {
+ // let branch = record.pg_info.pg_branch;
+ let _list = this.props.list.records || []
+ let style = {
+ display: 'show'
+ };
+ let listBody = _list.map((record, index) => {
+ let machine = record.machine_info[0];
+ let system = machine.os_name + ' ' + machine.os_version + ' ' + machine.comp_name + ' ' + machine.comp_version;
+ let alias = machine.alias;
+
+
+ let trend = record.trend
+ let improvedIconClassName = trend.improved > 0 ? 'improved' : 'anonymous'
+ let quoIconClassName = trend.quo > 0 ? 'quo' : 'anonymous'
+ let regressiveIconClassName = trend.regressive > 0 ? 'regressive' : 'anonymous'
+ return (
+
+ <Table.Row key={index}>
+ {/*alias*/}
+ <Table.Cell><a href="#">{alias}</a></Table.Cell>
+
+ {/*system*/}
+ <Table.Cell><a href="#">{system}</a></Table.Cell>
+
+ {/*branch*/}
+ {/*<Table.Cell>{branch}</Table.Cell>*/}
+
+ {/*trending-data*/}
+
+ <Table.Cell textAlign='center'>
+ <Icon className={"bgc-clear " + improvedIconClassName} name='smile outline' size='large'/>
+ <Bubble num={trend.improved} name="improved"/>
+ </Table.Cell>
+ <Table.Cell textAlign='center'>
+ <Icon className={"bgc-clear " + quoIconClassName} name='meh outline' size='large'/>
+ <Bubble num={trend.quo} name="quo"/>
+ </Table.Cell>
+ <Table.Cell textAlign='center'>
+ <Icon className={"bgc-clear " + regressiveIconClassName} name='frown outline'
+ size='large'/>
+ <Bubble num={trend.regressive} name="regressive"/>
+ </Table.Cell>
+
+
+ <Table.Cell textAlign='center'>
+ <Link color='linkedin' to={'detailInfo/' + record.uuid}>
+ <Icon name='linkify'/> Link
+ </Link>
+ </Table.Cell>
+
+
+ {/*date*/}
+ <Table.Cell>{record.add_time}</Table.Cell>
+ </Table.Row>
+ );
+ });
+
+ return (
+ <Table celled structured compact textAlign='center'>
+ <Table.Header>
+ {/*<Table.Row>*/}
+ {/*<Table.HeaderCell rowSpan='9'>Branch: 10_STABLE</Table.HeaderCell>*/}
+ {/*</Table.Row>*/}
+ <Table.Row>
+ <Table.HeaderCell rowSpan='2'>Alias</Table.HeaderCell>
+ <Table.HeaderCell rowSpan='2'>System</Table.HeaderCell>
+ {/*<Table.HeaderCell rowSpan='2'>Branch</Table.HeaderCell>*/}
+ <Table.HeaderCell colSpan='3'>Trending</Table.HeaderCell>
+ <Table.HeaderCell rowSpan='2'>Detail</Table.HeaderCell>
+ <Table.HeaderCell rowSpan='2'>Date</Table.HeaderCell>
+ </Table.Row>
+ <Table.Row>
+ <Table.HeaderCell>improvement</Table.HeaderCell>
+ <Table.HeaderCell>status quo</Table.HeaderCell>
+ <Table.HeaderCell>regression</Table.HeaderCell>
+ </Table.Row>
+
+ </Table.Header>
+
+ <Table.Body>
+ {listBody}
+ </Table.Body>
+ <Table.Footer>
+ <Table.Row>
+ <Table.HeaderCell colSpan='10'>
+ {/*<Menu size='small' floated='right' pagination>*/}
+ {/*<Menu.Item as='a' icon>*/}
+ {/*<Icon name='chevron left'/>*/}
+ {/*</Menu.Item>*/}
+ {/*<Menu.Item as='a'>1</Menu.Item>*/}
+ {/*<Menu.Item as='a'>2</Menu.Item>*/}
+ {/*<Menu.Item as='a'>3</Menu.Item>*/}
+ {/*<Menu.Item as='a'>4</Menu.Item>*/}
+ {/*<Menu.Item as='a' icon>*/}
+ {/*<Icon name='chevron right'/>*/}
+ {/*</Menu.Item>*/}
+ {/*</Menu>*/}
+ {/*<Pagination style={style} onChange={(current) => this.onPageNumChange(current)} pageSize={2}*/}
+ {/*current={this.state.currentPage} total={this.props.total}/>*/}
+
+ </Table.HeaderCell>
+
+ </Table.Row>
+ </Table.Footer>
+ </Table>
+ );
+
+ }
+
+
+}
+
+export default MachineRecordTable;
\ No newline at end of file
min-height: 0.1em!important;
padding: .2em!important;
line-height: 0.5em!important;
+}
+
+.link-div{
+ display: flex;
+ flex-direction: column;
}
\ No newline at end of file
// let improvedIconClassName = trend.improved > 0 ? 'improved' : 'anonymous'
// let quoIconClassName = trend.quo > 0 ? 'quo' : 'anonymous'
// let regressiveIconClassName = trend.regressive > 0 ? 'regressive' : 'anonymous'
+ let color = 'positive';
+ if(machine.state == 'pending'){
+ color = 'warning';
+ }else if(machine.state == 'prohibited'){
+ color = 'negative';
+ }
return (
-
- <Table.Row key={index}>
+ <Table.Row key={index} className={color}>
{/*alias*/}
<Table.Cell><a href="#">{machine.alias}</a></Table.Cell>
<Table.Cell><a href="#">{system}</a></Table.Cell>
{/*State*/}
- <Table.Cell>{machine.state}</Table.Cell>
+ <Table.Cell>
+ {machine.state}
+ </Table.Cell>
{/*lastest-records*/}
<Table.Cell textAlign='center'>
{/*<Icon className={"bgc-clear " + improvedIconClassName} name='smile outline' size='large'/>*/}
{/*<Bubble num={trend.improved} name="improved"/>*/}
- <LastestLink list={machine.lastest}/>
+ <div className="link-div">
+ <LastestLink list={machine.lastest}/>
+ </div>
</Table.Cell>
{/*machine history*/}
<Table.Cell textAlign='center'>
- <Link color='linkedin' to={'machineInfo/' + '###'}>
+ <Link color='linkedin' to={'machineInfo/' + machine.machine_sn}>
<Icon name='linkify'/> Link
</Link>
</Table.Cell>
<Table.Row>
<Table.HeaderCell colSpan='10'>
- <Pagination style={style} onChange={(current) => this.onPageNumChange(current)} pageSize={2}
+ <Pagination style={style} onChange={(current) => this.onPageNumChange(current)} pageSize={15}
current={this.state.currentPage} total={this.props.total}/>
</Table.HeaderCell>
console.dir(err.status)
// nologin force to login
if (PGConstant.UnauthorizedCode === err.status) {
+ this.removeStorage('userInfo');
this.doLogin();
} else {
- typeof reject === 'function' && reject(err.statusText);
+ typeof reject === 'function' && reject(err.status);
}
}
// redirect to login
doLogin() {
- window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname);
+ this.props.history.push('/login')
+ // window.location.href = '/login?redirect=' + encodeURIComponent(window.location.pathname);
// window.location.href = '/login';
}
from users.models import UserMachine
from django.db.models import Count
+
class TestBranchSerializer(serializers.ModelSerializer):
'''
use TestBranchSerializer
model = TestBranch
fields = ('branch_name',)
+
class TestCategorySerializer(serializers.ModelSerializer):
'''
use TestCategorySerializer
model = LinuxInfo
fields = ('cpuinfo', 'meminfo')
+
class LinuxInfoDetailSerializer(serializers.ModelSerializer):
'''
use LinuxInfoDetailSerializer
model = LinuxInfo
fields = ('mounts', 'sysctl')
+
class LinuxInfoSerializer(serializers.ModelSerializer):
'''
use LinuxInfoSerializer
class Meta:
model = MetaInfo
- fields = ('uname', )
+ fields = ('uname',)
+
class MetaInfoSerializer(serializers.ModelSerializer):
'''
use TestResultSerializer
'''
mode = serializers.SerializerMethodField()
+
class Meta:
model = TestResult
fields = "__all__"
+
def get_mode(self, obj):
new_dict = {v: k for k, v in DB_ENUM["mode"].items()}
return new_dict[obj.mode]
+
class CreateTestRecordSerializer(serializers.ModelSerializer):
'''
create ModelSerializer
# client_max_num = serializers.SerializerMethodField()
class Meta:
model = TestRecord
- fields = ('uuid', 'add_time', 'machine_info', 'pg_info', 'branch','trend', 'linux_info', 'meta_info')
+ fields = ('uuid', 'add_time', 'machine_info', 'pg_info', 'branch', 'trend', 'linux_info', 'meta_info')
def get_branch(self, obj):
branch = TestBranch.objects.filter(id=obj.branch.id).first()
class TestDataSetDetailSerializer(serializers.ModelSerializer):
results = serializers.SerializerMethodField()
+
class Meta:
model = TestDataSet
fields = "__all__"
class Meta:
model = TestRecord
fields = (
- 'branch', 'date', 'uuid', 'pg_info', 'linux_info', 'hardware_info', 'meta_info', 'dataset_info', 'test_desc', 'meta_time', 'test_machine')
+ 'branch', 'date', 'uuid', 'pg_info', 'linux_info', 'hardware_info', 'meta_info', 'dataset_info',
+ 'test_desc', 'meta_time', 'test_machine')
def get_branch(self, obj):
branch = TestBranch.objects.filter(id=obj.branch_id).first()
# rw_info_serializer = TestResultSerializer(all_data, many=True, context={'request': self.context['request']})
# return rw_info_serializer.data
+
class MachineHistoryRecordSerializer(serializers.ModelSerializer):
'''
use MachineHistoryRecordSerializer
fields = ('machine_info', 'reports')
def get_reports(self, obj):
- target_records = TestRecord.objects.filter(test_machine_id=obj.id)
- serializer = TestRecordListSerializer(target_records,many=True)
+ target_records = TestRecord.objects.filter(test_machine_id=obj.id).values_list(
+ 'branch').annotate(Count('id'))
+ # print(target_records) # <QuerySet [(2, 2), (1, 3)]>
+ ret = []
+ for branch_item in target_records:
+ item = {}
+ item['branch'] = branch_item[0]
- return serializer.data
+ records = TestRecord.objects.filter(test_machine_id=obj.id,branch_id=branch_item[0])
+
+ serializer = TestRecordListSerializer(records, many=True)
+ item['records'] = serializer.data
+ ret.append(item)
+ return ret
def get_machine_info(self, obj):
target_machine = UserMachine.objects.filter(id=obj.id).first()
serializer = UserMachineSerializer(target_machine)
- return serializer.data
\ No newline at end of file
+ return serializer.data
serializer_class = TestRecordDetailSerializer
# pagination_class = StandardResultsSetPagination
-class MachineHistoryRecordViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
+class MachineHistoryRecordViewSet( mixins.RetrieveModelMixin, viewsets.GenericViewSet):
"""
detail test records
"""
state = serializers.SerializerMethodField()
class Meta:
model = UserMachine
- fields = ('alias', 'os_name', 'os_version', 'comp_name', 'comp_version', 'reports', 'state', 'lastest', 'state', 'add_time')
+ fields = ('alias', 'machine_sn','os_name', 'os_version', 'comp_name', 'comp_version', 'reports', 'state', 'lastest', 'state', 'add_time')
def get_state(self, obj):
state_code = obj.state
router = DefaultRouter()
router.register(r'records', TestRecordListViewSet, base_name="records")
router.register(r'detail', TestRecordDetailViewSet, base_name="detail")
-router.register(r'machine', MachineHistoryRecordViewSet, base_name="machine")
+router.register(r'machine-records', MachineHistoryRecordViewSet, base_name="machine-records")
# user's machine manage list