And so, I decided that I should have two resources: /selectionboxes that provides the entire application and /selected, which provides access to which names stay on the left or right boxes. So, I have the following operations:
1 - GET /selected
[
[{"age": 21, "name": "Paulo"}, {"age": 25, "name": "Sandra"}, {"age": 16, "name": "Pinto"}, {"age": 18, "name": "Saul"}, {"age": 11, "name": "Camilo"}, {"age": 9, "name": "Luis"}, {"age": 21, "name": "Andre"}],
[{"age": 15, "name": "Rui"}, {"age": 23, "name": "Adelle"}, {"age": 50, "name": "Bryan"}, {"age": 40, "name": "Castro"}]
]
First list has the names that are not selected (left box); the second list has the names that are selected (right box)
2 - POST /selected with parameter 'person'
This adds a person to the selected list (i.e., moves it from the left to the right side in the previous listing)
3 - DELETE /selected/Saul
This deletes the name 'Saul' from the selected list, passing it to the other, list of non-selected names (i.e, from the right to the left)
I could also have an additional service to list a particular person, which would look like:
4 - GET /selected/Saul
giving me {"age": 18, "name": "Saul"}, but I don't need it, in this exercise. So, I don't have this.
I removed some functions from the server that were related to other exercises, but the overall result is shorter, and hopefully cleaner. The parts of interest are in the 'operateSelected'. I also handle possible ValueError exceptions:
'''
Created on 28/03/2016
@author: filipius
'''
from flask import Flask, request, Response
import json
app = Flask(__name__)
listofpeople = [[], []]
@app.errorhandler(ValueError)
def handle_invalid_usage(error):
response = str(error)
return response, 400
def readpeople(filename):
with open(filename, 'rt', encoding='utf-8') as f:
result = []
lines = f.readlines()
for l in lines:
array = l.strip().split()
name, age = array[0], int(array[1])
result.append({'name' : name, 'age': age})
return result
def intersection(l1,
l2):
return [val for val in l1 if val in l2]
def subtract(l1,
l2):
return [val for val in l1 if val not in l2]
def updateListOfPeople():
newlistofpeople = readpeople('names.txt')
listofpeople[0] =
subtract(newlistofpeople, listofpeople[1])
listofpeople[1] =
intersection(listofpeople[1], newlistofpeople)
def persontolist(name,
peoplist):
for p in peoplist:
if (p['name'] == name):
return p
raise ValueError('No person called ' + name + ' exists')
#listofpeople[0] --->
non-selected
#listofpeople[1] --->
selected
@app.route('/selected', methods=['GET', 'POST'])
@app.route('/selected/<person>', methods=['DELETE'])
def operateSelected(person=None):
print('Not
thread-safe! Please correct me!')
if person == None:
if request.method == 'POST' and 'person' in request.form and request.form['person'] != '':
#method == 'POST' add people to selected
theperson =
persontolist(request.form['person'], listofpeople[0])
listofpeople[0].remove(theperson)
listofpeople[1].append(theperson)
else:
if request.method == 'DELETE' and person != 'null': #sub people from
selected
theperson = persontolist(person,
listofpeople[1])
listofpeople[1].remove(theperson)
listofpeople[0].append(theperson)
updateListOfPeople()
return
Response(response=json.dumps(listofpeople), status=200, mimetype="application/json")
@app.route('/selectionboxes')
def getSelectHtml4():
return app.send_static_file('react4/selectpeople4.html')
if __name__ == '__main__':
app.run(debug=True, port=5000)
|
Slight differences exist in the React code as well, e.g., to invoke the DELETE operation.
var Table =
React.createClass({
getInitialState
: function() {
return {people : [[], []], toadd : null, tosub : null};
},
gotResponse
: function(data) {
this.setState({people : data})
},
loadPeopleFromServer
: function() {
console.log('loadPeopleFromServer. url = ' + this.props.baseurl);
$.ajax({
url:
this.props.baseurl,
dataType:
'json',
cache:
false,
success:
this.gotResponse,
error:
function(xhr, status,
err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
add
: function() {
if (this.state.toadd != null) {
var posting = $.post(this.props.baseurl, {person : this.state.toadd});
posting.done(this.gotResponse);
this.setState({toadd : null});
}
},
sub
: function() {
if (this.state.tosub != null) {
$.ajax({
url: this.props.baseurl + '/' + this.state.tosub,
type: 'DELETE',
success: this.gotResponse,
error:
function(xhr, status,
err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
this.setState({tosub : null});
}
},
changeAdd
: function(person) {
this.setState({toadd : person})
},
changeSub
: function(person) {
this.setState({tosub : person})
},
componentDidMount
: function() {
this.loadPeopleFromServer();
this.timer = setInterval(this.loadPeopleFromServer, 10000);
},
componentWillUnmount
: function() {
clearInterval(this.timer);
},
render
: function() {
return (
<table>
<tbody>
<tr>
<td>
<h1>Add:</h1>
</td>
<td
/>
<td>
<h1>Remove:</h1>
</td>
</tr>
<tr>
<td>
<PeopleList
peoplelist={this.state.people[0]} change={this.changeAdd} value={this.state.toadd} name="from"/>
</td>
<td>
<SelectButton
handleclick={this.add} title="-->"/>
<br/>
<SelectButton
handleclick={this.sub} title="<--"/>
</td>
<td>
<PeopleList
peoplelist={this.state.people[1]} change={this.changeSub} value={this.state.tosub} name="to"/>
</td>
</tr>
</tbody>
</table>
);
}
});
var SelectButton = React.createClass({
render
: function() {
return (<button onClick={this.props.handleclick} type="button">{this.props.title}</button>);
}
});
var PeopleList = React.createClass({
change
: function(event) {
this.props.change(event.target.value);
},
render
: function() {
var theoptions = [];
for (var i = 0; i < this.props.peoplelist.length; i++) {
var person = this.props.peoplelist[i];
theoptions.push(<option
value={person.name} key={person.name}>{person.name},
{person.age}</option>);
}
return (
<div>
<select
onChange={this.change} value={this.props.value} size="5">
{theoptions}
</select>
</div>
);
},
})
ReactDOM.render(<Table baseurl="/selected"/>, mountNode);
|
Good luck!