Develop a REST server script named measurement_server.py. This script will respo
ID: 3817281 • Letter: D
Question
Develop a REST server script named measurement_server.py. This script will respond to requests for data from the measurement database by sending back data that is JSON serialized. The server should listen on any interface at a particular port number.
Here are the paths and the requests that they correspond to
Path------------------------------------------------------------------- Function
/area----------------------------------------------------------Get a list of all areas
/area/(d+)/location----------------------------------------Get all locations for the given area id
/location/(d+)/measurement----------------------------Get all the measurements for the given location id
/area/(d+)/category---------------------------------------Get all the categories to which the given area belongs
/area/(d+)/average_measurement--------------------Get the average measurement for the given area
/area/(d+)/number_locations---------------------------Get the number of locations in the given area
Notes: • In the first four request types, return a list of dictionaries, JSON encoded
• In the last two request types, return the numbers. JSON encoded
You may wish to follow the organization used in the sakila_rest example in class.
Overview
This assignment will use the measurements example database as a subject. You will be providing data from this database as a service using REST. The measurements example is described in the Measurements Example.pdf. But, please note! Use the file measures.sqlite rather than recreate the file yourself.
Test Setup
The project “assign05_test.zip” contains a short test. The file test2.py is the test script, the other files are support. Note: the testing files should not be in the same directory with the server. Link to all the file required: http://ksuweb.kennesaw.edu/~bsetzer/4720su16/nanoc/output/assignments/5/
Explanation / Answer
reader_writer.py
import re
"""
This provides reading and writing of characters for sockets.
This is already provided by the Python library, but it looked interesting
to try working it out directly.
"""
class reader_writer:
"""
Provides reading and writing character services.
Coding and decoding is handled.
Data is transmitted in packets so there should be no decoding anomalies.
"""
char_chunk_size = 128
byte_buffer_size = 16*char_chunk_size
def __init__(self, socket, encoding='utf-8'):
self.socket = socket
self.in_char_buffer = ""
#self.out_char_buffer = ""
self.stream_done = False
self.encoding = encoding
def read(self):
"""
read one character and return
return "" if the stream is done
:return: the character read
"""
if len(self.in_char_buffer) == 0:
self.fill_char_buffer()
if self.stream_done:
return ""
if len(self.in_char_buffer) > 0:
rtval = self.in_char_buffer[0]
self.in_char_buffer = self.in_char_buffer[1:]
return rtval
else:
return ""
def unread(self, ch):
self.in_char_buffer = ch + self.in_char_buffer
def fill_char_buffer(self):
"""
Get some more bytes for the data buffer
"""
data_bytes = self.socket.recv(reader_writer.byte_buffer_size)
#print("Server received data bytes %d" % len(data_bytes))
if len(data_bytes) > 0:
self.in_char_buffer += data_bytes.decode(self.encoding)
else:
self.stream_done = True
def write(self, data):
"""
write a string to the socket.
"""
self.socket.sendall(data.encode(self.encoding))
# i = 0
# while i < len(data):
# data_bytes = data[i:i+reader_writer.char_chunk_size].encode(self.encoding
# self.socket.send(data_bytes)
# i += reader_writer.char_chunk_size
# #print(" block sent ")
def next_word(self):
"""
Get the next word from the input stream of the socket.
:return: the next word, empty if the input is exhausted
"""
c = self.read()
while re.match(r's', c):
c = self.read()
if len(c) > 0:
word = c
c = self.read()
while re.match(r'S', c):
word += c
c = self.read()
return word
else:
return ''
def close_read(self):
self.socket.shutdown(0)
def close_write(self):
self.socket.shutdown(1)
def encoded_length(self, message):
return len(message.encode(self.encoding))
main.py
from socket import socket
import json
import re
import logging
def server_port():
return 12345
if __name__ == "__main__":
logging.basicConfig(filename='example.log', level=logging.INFO)
# setting up a listener socket
sock = socket()
sock.bind(('', server_port()))
sock.listen(0) # 0 backlog of connections
ACCEPTABLE_REQUEST_TYPES = [re.compile('/area'),
re.compile('/area/(d+)/location'),
re.compile('/area/(d+)/category'),
re.compile('/area/(d+)/average_measurement'),
re.compile('/area/(d+)/number_locations'),
re.compile('/location/(d+)/measurement')]
while True:
(conn, address) = sock.accept()
rw = reader_writer(conn)
logging.info('Connection made: {}'.format(conn))
first_line = read_line(rw)
logging.info('Client request: {}'.format(first_line))
response = ''
if any(re.search(regex, first_line) for regex in ACCEPTABLE_REQUEST_TYPES):
if re.search(ACCEPTABLE_REQUEST_TYPES[5], first_line):
ml_id = re.split('/', first_line)
response = db.do_command('SELECT * FROM measurement WHERE measurement_location=?', [ml_id[2]])
elif re.search(ACCEPTABLE_REQUEST_TYPES[4], first_line):
la_id = re.split('/', first_line)
response = db.do_command('SELECT COUNT(*) AS Number_of_Locations FROM location WHERE location_area=?', [la_id[2]])
elif re.search(ACCEPTABLE_REQUEST_TYPES[3], first_line):
am_id = re.split('/', first_line)
response = db.do_command('SELECT AVG(value) AS Average_Measurement FROM measurement WHERE measurement_location=?', [am_id[2]])
elif re.search(ACCEPTABLE_REQUEST_TYPES[2], first_line):
cat_id = re.split('/', first_line)
response = db.do_command('SELECT name from category NATURAL JOIN category_area WHERE area_id=?', [cat_id[2]])
elif re.search(ACCEPTABLE_REQUEST_TYPES[1], first_line):
loc_id = re.split('/', first_line)
response = db.do_command('SELECT name, altitude FROM location WHERE location_area=?', [loc_id[2]])
elif re.search(ACCEPTABLE_REQUEST_TYPES[0], first_line):
response = db.do_command('SELECT * FROM area')
logging.info('Server response: {}'.format(response))
send_binary_response(rw, json.dumps(response).encode())
else:
logging.info('Bad request: {}: sending failed response'.format(first_line))
send_binary_response(rw, "You have requested an unsupported request".encode(), status=400, status_remark='Bad Request')
conn.shutdown(1) ## shutdown the sending side
conn.close()
logging.info("Closed connection")
socket_util.py
def read_line(rw):
"""
Reads and returns one line from rw
Line endings could be or
:param rw: reader_writer
:return:
"""
the_line = ""
ch = rw.read()
while ch != ' ' and ch != ' ':
the_line += ch
ch = rw.read()
ch = rw.read() # is this /n?
if ch != ' ' and ch != ' ':
rw.unread(ch)
return the_line
def send_binary_response(rw, data, content_type='text/plain', status =200, status_remark='OK'):
"""
:param rw: A reader_writer to use to send the response
:param message:
:param content_type:
:param status:
:param status_remark:
:return:
"""
# first line
# content-type
# content-length (bytes)
# blank line
# content
rw.write("HTTP/1.1 {} {} ".format(status, status_remark))
rw.write("Content-type: {} ".format(content_type))
rw.write("Content-length: {} ".format(len(data)))
rw.write(" ")
rw.socket.sendall(data)
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.