React.js work with Tornado

React work with tornado.

I would like to understand how React.js work, so just try to do it…

Based on Ubuntu 14.04 64bit
Prerequisite : node.js, webpack, tornado, fixed-data-table.

Flow

Run Tornado -> regularly get XML data from another website -> display with React

Install

  1. mkdir react-table && cd react-table
  2. npm init
  3. npm install webpack –save-dev
  4. npm install babel-loader babel-core babel-preset-es2015 babel-preset-react –save-dev
  5. ln -s /usr/bin/nodejs /usr/bin/node
  6. npm install –save react
  7. npm install –save react-dom
  8. npm install webpack-dev-server –save-dev
  9. npm install fixed-data-table –save
  10. npm install css-loader style-loader –save-dev

React part

touch webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* webpack.config.js */
var path = require('path');
var webpack = require('webpack');

module.exports = {
context: __dirname + "/src",
entry: "./app.js",
output: {
filename: "app.js",
path: __dirname + "/dist",
},
module: {
loaders: [
{
test: /\.jsx?$/,
include: [
__dirname + "/src"
],
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['react', 'es2015']
}
}, { test: /\.css$/, loader: 'style-loader!css-loader' },
]
},
};

touch myTable.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* myTable.js */
"use strict";
var FakeObjectDataListStore = require('./dataListStore');
var FixedDataTable = require('fixed-data-table');
var React = require('react');

const {Table, Column, Cell} = FixedDataTable;
const TextCell = ({rowIndex, data, col, ...props}) => (
<Cell {...props}>
{data.getObjectAt(rowIndex)[col]}
</Cell>
);

class MyLinkCell extends React.Component {
render() {
const {rowIndex, data, col, ...props} = this.props;
const link = data.getObjectAt(rowIndex)[col];
return (
<Cell {...props}>
<a href={link}>{link}</a>
</Cell>
);

}
}
class DataListWrapper {
constructor(indexMap, data) {
this._indexMap = indexMap;
this._data = data;
}
getSize() {
return this._indexMap.length;
}
getObjectAt(index) {
return this._data.getObjectAt(
this._indexMap[index]
);
}
}
class MyTable extends React.Component {
constructor(props) {
super(props);
//=========================
var pageData = JSON.parse(document.getElementById('page-data').getAttribute('data-page'));
//========================
this._dataList = new FakeObjectDataListStore(pageData);
this.state = {
filteredDataList: this._dataList,
};
this._onFilterChange = this._onFilterChange.bind(this);
}
_onFilterChange(e) {
if (!e.target.value) {
this.setState({
filteredDataList: this._dataList,
});
}
var filterBy = e.target.value;
var size = this._dataList.getSize();
var filteredIndexes = [];
for (var index = 0; index < size; index++) {
var {TITLE} = this._dataList.getObjectAt(index);
if (TITLE.indexOf(filterBy) !== -1){
filteredIndexes.push(index);
}
}
this.setState({
filteredDataList: new DataListWrapper(filteredIndexes, this._dataList),
});
}
render() {
var {filteredDataList} = this.state;
return (
<div>
<input
onChange={this._onFilterChange}
placeholder="Filter by 職稱"
/>

<br />
<Table
rowHeight={50}
rowsCount={filteredDataList.getSize()}
headerHeight={50}
width={1000}
height={500}
{...this.props}>

<Column
header={<Cell>職稱</Cell>}

cell={<TextCell data={filteredDataList} col="TITLE" />}
fixed={true}
width={100}
/>
<Column
header={<Cell>用人單位</Cell>}

cell={<TextCell data={filteredDataList} col="ORG_NAME" />}
fixed={true}
width={100}
/>
<Column
header={<Cell>工作區域</Cell>}

cell={<TextCell data={filteredDataList} col="WORK_PLACE_TYPE" />}
width={100}
/>
<Column
header={<Cell>人員區分</Cell>}

cell={<TextCell data={filteredDataList} col="GENDER_TYPE" />}
width={100}
/>
<Column
header={<Cell>開始日</Cell>}

cell={<TextCell data={filteredDataList} col="DATE_FROM" />}
width={100}
/>
<Column
header={<Cell>結束日</Cell>}

cell={<TextCell data={filteredDataList} col="DATE_TO" />}
width={100}
/>
<Column
header={<Cell>網址</Cell>}

cell={<MyLinkCell data={filteredDataList} col="VIEW_URL" />}
width={400}
/>
</Table>
</div>
);

}
}
module.exports = MyTable;

touch dataListStore.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* dataListStore.js */
class dataListStore {
constructor( rows ){
this.size = rows.length;
this._cache = rows;
}
getObjectAt(/*number*/ index) /*?object*/ {
if (index < 0 || index > this.size){
console.log('_QQca -> ',this._cache[index],index);
return undefined;
}
if (this._cache[index] === undefined) {
console.log('_cache undefined!!!!! ');
}
return this._cache[index];
}
getAll() {
if (this._cache.length < this.size) {
for (var i = 0; i < this.size; i++) {
this.getObjectAt(i);
}
}
return this._cache.slice();
}
getSize() {
return this.size;
}
}
module.exports = dataListStore;

touch app.js

1
2
3
4
5
6
/* app.js */
import MyTable from './myTable'
import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(<MyTable />,document.getElementById('example'));

run webpack
root@ubuntu:/home/jeff/react-table# webpack

copy app.js to Tornado part
root@ubuntu:/home/jeff/react-table# mkdir -p ../react-tornado/static/js && mkdir ../react-tornado/static/css && mkdir -p ../react-tornado/templates
root@ubuntu:/home/jeff/react-table# cp fixed-data-table.css ../react-tornado/static/css/
root@ubuntu:/home/jeff/react-table# cp ./dist/app.js ../react-tornado/static/js/

Tornado part

/react-tornado

touch /react-tornado/templates/index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Govjobs</title>
<link href="/static/css/fixed-data-table.css"
rel="stylesheet">

</head>
<body style="font-family:arial;font-size:12px; background:#e1e1e1">
<p></p>
<div id="example"></div>
<div id="page-data" data-page=""></div>
<script src="/static/js/app.js"></script>
</body>
</html>

touch /react-tornado/main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# main.py
# -*- coding: utf-8 -*-
import os,json, time, datetime
import tornado.httpserver
import tornado.options
from tornado import gen
from tornado import web, ioloop, websocket
###
import xml.etree.cElementTree as ET
from apscheduler.schedulers.background import BackgroundScheduler
import urllib3
###

settings = {
"static_path": os.path.join(os.path.dirname(__file__), "static"),
"template_path": os.path.join(os.path.dirname(__file__), "templates"),
"autoreload": True
}

class graphHandler(tornado.web.RequestHandler):
@gen.coroutine
def i(self):
return self.loadData()
@gen.coroutine
def get(self):
response, announceDate = yield self.i()
self.render("index.html", a=response,b=announceDate)
def loadData(self):
#saveData()
module_dir = os.path.dirname(__file__)
file_path = os.path.join(module_dir, 'export.xml')
A=[]
tree = ET.parse(file_path)
root = tree.getroot()
ANNOUNCE_DATE = root.find('ANNOUNCE_DATE').text
for country in root.findall('ROW'):
A.append({
'TITLE' : country.find('TITLE').text,
'ORG_NAME' : country.find('ORG_NAME').text,
'WORK_PLACE_TYPE' : country.find('WORK_PLACE_TYPE').text,
'GENDER_TYPE' : country.find('GENDER_TYPE').text,
'DATE_FROM' : country.find('DATE_FROM').text,
'DATE_TO' : country.find('DATE_TO').text,
'VIEW_URL' : country.find('VIEW_URL').text
})
return json.dumps(A, ensure_ascii=False),ANNOUNCE_DATE

def saveData():
http = urllib3.PoolManager()
r = http.request('GET', 'http://web3.dgpa.gov.tw//WANT03FRONT//AP//WANTF00003.aspx?GETJOB=Y')
if r.status == 200:
module_dir = os.path.dirname(__file__)
file_path = os.path.join(module_dir, 'export.xml')
with open(file_path, "wb") as code:
code.write(r.data)

sched = BackgroundScheduler()

def scheduled_job():
saveData()

def main():
application = tornado.web.Application([
(r"/", graphHandler)
], **settings)

http_server = tornado.httpserver.HTTPServer(application)
port = int(os.environ.get("PORT", 5000))
http_server.listen(port)

sched.add_job(scheduled_job, 'cron', day_of_week='mon-fri', hour=18, minute=00)
sched.start()

tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
main()

Run
python main.py

Demo

There are troubles to get data regularly on heroku has some limits on http request and the file save…

https://govjobs.herokuapp.com/

Work fine on local

 show result