1
0
Fork 0
Browse Source

add device status-update logging.

pull/7/head
Craig Oates 4 years ago
parent
commit
641fe8f604
  1. 5
      app/api.py
  2. 35
      app/build_database.py
  3. 82
      app/models/devices.py
  4. BIN
      app/readings.db
  5. BIN
      app/readings.db-journal
  6. 4
      app/services/get_services.py
  7. 90
      app/services/post_services.py
  8. 59
      app/swagger.yml

5
app/api.py

@ -10,7 +10,10 @@ These functions are acting as very light controllers essentially.
''' '''
def post_a_reading(light_meter, the_reading): def post_a_reading(light_meter, the_reading):
return post_services.add_latest_reading(light_meter,the_reading) return post_services.add_latest_reading(light_meter, the_reading)
def post_a_status_change(device, the_status_change):
return post_services.log_status_change(device, the_status_change)
def get_latest(light_meter): def get_latest(light_meter):
return get_services.get_latest_reading(light_meter) return get_services.get_latest_reading(light_meter)

35
app/build_database.py

@ -2,6 +2,7 @@ import os
from datetime import datetime from datetime import datetime
from config import db from config import db
from models.meters import Meter1, Meter2, Meter3 from models.meters import Meter1, Meter2, Meter3
from models.devices import Device1, Device2, Device3, Device4, Device5, Device6
def get_timestamp(): def get_timestamp():
return datetime.now().strftime(("%Y-%m-%d %H:%M:%S")) return datetime.now().strftime(("%Y-%m-%d %H:%M:%S"))
@ -10,6 +11,12 @@ def get_timestamp():
READINGS1 =[ {"time":datetime.now(), "reading": 0} ] READINGS1 =[ {"time":datetime.now(), "reading": 0} ]
READINGS2 =[ {"time":datetime.now(), "reading": 0} ] READINGS2 =[ {"time":datetime.now(), "reading": 0} ]
READINGS3 =[ {"time":datetime.now(), "reading": 0} ] READINGS3 =[ {"time":datetime.now(), "reading": 0} ]
DEVICE1 = [ {"time":datetime.now(), "status": "off"} ]
DEVICE2 = [ {"time":datetime.now(), "status": "off"} ]
DEVICE3 = [ {"time":datetime.now(), "status": "off"} ]
DEVICE4 = [ {"time":datetime.now(), "status": "off"} ]
DEVICE5 = [ {"time":datetime.now(), "status": "off"} ]
DEVICE6 = [ {"time":datetime.now(), "status": "off"} ]
# Deletes the database if it already exists # Deletes the database if it already exists
if os.path.exists("readings.db"): if os.path.exists("readings.db"):
@ -28,9 +35,35 @@ for info in READINGS2:
r = Meter2(time=info.get("time"), reading=info.get("reading")) r = Meter2(time=info.get("time"), reading=info.get("reading"))
db.session.add(r) db.session.add(r)
# Iterates over the READINGS3 structure and populates the datebase # Iterates over the READINGS3 structure and populates the datebase.
for info in READINGS3: for info in READINGS3:
r = Meter3(time=info.get("time"), reading=info.get("reading")) r = Meter3(time=info.get("time"), reading=info.get("reading"))
db.session.add(r) db.session.add(r)
# These are the light-meters in Ritherdon.
for info in DEVICE1:
r = Device1(time=info.get("time"), status=info.get("status"))
db.session.add(r)
for info in DEVICE2:
r = Device2(time=info.get("time"), status=info.get("status"))
db.session.add(r)
for info in DEVICE3:
r = Device3(time=info.get("time"), status=info.get("status"))
db.session.add(r)
# These are the relays in the gallery.
for info in DEVICE4:
r = Device4(time=info.get("time"), status=info.get("status"))
db.session.add(r)
for info in DEVICE5:
r = Device5(time=info.get("time"), status=info.get("status"))
db.session.add(r)
for info in DEVICE6:
r = Device6(time=info.get("time"), status=info.get("status"))
db.session.add(r)
db.session.commit() db.session.commit()

82
app/models/devices.py

@ -0,0 +1,82 @@
from datetime import datetime
from config import db, ma
'''
Note on Duplication Levels
======================================================================
While the code in this file seems very duplicated, it is just the
result of using SQL-Alchemy (ORM) and the repetitive nature of the
project as a whole. At the time of writing, the expected amount of
devices is six. They must keep their info. separate from each other.
This means a table in the database for each device. This is the main
cause for the repetitive/duplicated code. Because this project has
fixed requirements, the hard-coded nature is a trade-off because of
this. If the project increases the amount of devices it uses, this
will probably need to be refactored.
'''
class Device1(db.Model):
__tablename__ = "device1"
id = db.Column(db.Integer, primary_key=True)
time = db.Column(db.DateTime, default=datetime.utcnow)
status = db.Column(db.String)
class Device1Schema(ma.ModelSchema):
class Meta:
model = Device1
sqla_session = db.session
class Device2(db.Model):
__tablename__ = "device2"
id = db.Column(db.Integer, primary_key=True)
time = db.Column(db.DateTime, default=datetime.utcnow)
status = db.Column(db.String)
class Device2Schema(ma.ModelSchema):
class Meta:
model = Device2
sqla_session = db.session
class Device3(db.Model):
__tablename__ = "device3"
id = db.Column(db.Integer, primary_key=True)
time = db.Column(db.DateTime, default=datetime.utcnow)
status = db.Column(db.String)
class Device3Schema(ma.ModelSchema):
class Meta:
model = Device1
sqla_session = db.session
class Device4(db.Model):
__tablename__ = "device4"
id = db.Column(db.Integer, primary_key=True)
time = db.Column(db.DateTime, default=datetime.utcnow)
status = db.Column(db.String)
class Device4Schema(ma.ModelSchema):
class Meta:
model = Device1
sqla_session = db.session
class Device5(db.Model):
__tablename__ = "device5"
id = db.Column(db.Integer, primary_key=True)
time = db.Column(db.DateTime, default=datetime.utcnow)
status = db.Column(db.String)
class Device5Schema(ma.ModelSchema):
class Meta:
model = Device1
sqla_session = db.session
class Device6(db.Model):
__tablename__ = "device6"
id = db.Column(db.Integer, primary_key=True)
time = db.Column(db.DateTime, default=datetime.utcnow)
status = db.Column(db.String)
class Device6Schema(ma.ModelSchema):
class Meta:
model = Device1
sqla_session = db.session

BIN
app/readings.db

Binary file not shown.

BIN
app/readings.db-journal

Binary file not shown.

4
app/services/get_services.py

@ -12,6 +12,8 @@ at Ritherdon, you will need to head to the /post_services.py/ file.
It should be in the same directory at this: /services/. It should be in the same directory at this: /services/.
''' '''
bad_meter_id_message = "Meter Id. not recognised. Must be between 1 and 3."
def get_latest_reading(meter): def get_latest_reading(meter):
if meter == 1: if meter == 1:
return get_m1_latest() return get_m1_latest()
@ -19,6 +21,7 @@ def get_latest_reading(meter):
return get_m2_latest() return get_m2_latest()
elif meter == 3: elif meter == 3:
return get_m3_latest() return get_m3_latest()
return make_response(bad_meter_id_message, 400)
def get_all_readings_from_table(name): def get_all_readings_from_table(name):
if name == 1: if name == 1:
@ -27,6 +30,7 @@ def get_all_readings_from_table(name):
return get_all_readings_for_meter2() return get_all_readings_for_meter2()
elif name == 3: elif name == 3:
return get_all_readings_for_meter3() return get_all_readings_for_meter3()
return make_response(bad_meter_id_message, 400)
def get_all_readings_from_database(): def get_all_readings_from_database():
return get_all_readings() return get_all_readings()

90
app/services/post_services.py

@ -2,14 +2,18 @@ from flask import make_response, abort
from config import db from config import db
from models.meters import (Meter1, Meter1Schema, Meter2, Meter2Schema, from models.meters import (Meter1, Meter1Schema, Meter2, Meter2Schema,
Meter3, Meter3Schema) Meter3, Meter3Schema)
from models.devices import (Device1, Device1Schema, Device2, Device2Schema,
Device3, Device3Schema, Device4, Device4Schema,
Device5, Device5Schema, Device6, Device6Schema)
''' '''
Post Services Note Post Services Note
====================================================================== ======================================================================
The functions in this file are for storing the readings taken from the The functions in this file are for storing the readings taken from the
light meters in Ritherdon. It you are wanting to retrieve data from light meters in Ritherdon, and log changes in state for any of the
the (this) server, you will need to head to the /get_services.py/ file. devices. It you are wanting to retrieve data from the (this) server,
It should be in the same directory as this: /services/. you will need to head to the /get_services.py/ file. It should be in
the same directory as this: /services/.
''' '''
def add_latest_reading(meter,reading): def add_latest_reading(meter,reading):
@ -19,24 +23,96 @@ def add_latest_reading(meter,reading):
return add_reading_to_meter2(reading) return add_reading_to_meter2(reading)
elif meter == 3: elif meter == 3:
return add_reading_to_meter3(reading) return add_reading_to_meter3(reading)
return make_response("Meter Id. not recognised. Must be between 1 and 3.", 400)
def log_status_change(device, status):
if device == 1:
return add_status_change_to_device1(status)
elif device == 2:
return add_status_change_to_device2(status)
elif device == 3:
return add_status_change_to_device3(status)
elif device == 4:
return add_status_change_to_device4(status)
elif device == 5:
return add_status_change_to_device5(status)
elif device == 6:
return add_status_change_to_device6(status)
return make_response("Device Id. not recognised. Must be between 1 and 6.", 400)
'''
Nitty-Gritty Functions
======================================================================
The functions above are basically "header" functions. It makes it
easier to have multiple files open at once and see what this module
provides function-wise. The functions below do the real work in this
file. Please keep the "public" functions about this comment and defer
the main work to here. The "public" functions should be as thin as
possible to make them as scanable as possible.
'''
reading_message = "Reading successfully stored in database."
status_message = "Status change successfully logged in database."
def add_reading_to_meter1(the_reading): def add_reading_to_meter1(the_reading):
schema = Meter1Schema() schema = Meter1Schema()
new_reading = schema.load(the_reading, session=db.session) new_reading = schema.load(the_reading, session=db.session)
db.session.add(new_reading) db.session.add(new_reading)
db.session.commit() db.session.commit()
return make_response("Reading successfully stored in database.", 201) return make_response(reading_message, 201)
def add_reading_to_meter2(the_reading): def add_reading_to_meter2(the_reading):
schema = Meter2Schema() schema = Meter2Schema()
new_reading = schema.load(the_reading, session=db.session) new_reading = schema.load(the_reading, session=db.session)
db.session.add(new_reading) db.session.add(new_reading)
db.session.commit() db.session.commit()
return make_response("Reading successfully stored database.", 201) return make_response(reading_message, 201)
def add_reading_to_meter3(the_reading): def add_reading_to_meter3(the_reading):
schema = Meter3Schema() schema = Meter3Schema()
new_reading = schema.load(the_reading, session=db.session) new_reading = schema.load(the_reading, session=db.session)
db.session.add(new_reading) db.session.add(new_reading)
db.session.commit() db.session.commit()
return make_response("Reading successfully stored database.", 201) return make_response(reading_message, 201)
def add_status_change_to_device1(status):
schema = Device1Schema()
new_status = schema.load(status, session=db.session)
db.session.add(new_status)
db.session.commit()
return make_response(status_message, 201)
def add_status_change_to_device2(status):
schema = Device2Schema()
new_status = schema.load(status, session=db.session)
db.session.add(new_status)
db.session.commit()
return make_response(status_message, 201)
def add_status_change_to_device3(status):
schema = Device3Schema()
new_status = schema.load(status, session=db.session)
db.session.add(new_status)
db.session.commit()
return make_response(status_message, 201)
def add_status_change_to_device4(status):
schema = Device4Schema()
new_status = schema.load(status, session=db.session)
db.session.add(new_status)
db.session.commit()
return make_response(status_message, 201)
def add_status_change_to_device5(status):
schema = Device5Schema()
new_status = schema.load(status, session=db.session)
db.session.add(new_status)
db.session.commit()
return make_response(status_message, 201)
def add_status_change_to_device6(status):
schema = Device6Schema()
new_status = schema.load(status, session=db.session)
db.session.add(new_status)
db.session.commit()
return make_response(status_message, 201)

59
app/swagger.yml

@ -16,7 +16,7 @@ paths:
post: post:
operationId: api.post_a_reading operationId: api.post_a_reading
tags: tags:
- Store Readings - Log Light-Meter Readings
summary: >- summary: >-
Posts a reading from one of the project's light meters and store Posts a reading from one of the project's light meters and store
it in the database. it in the database.
@ -54,7 +54,7 @@ paths:
time: time:
type: string type: string
format: date-time format: date-time
example: 2019-10-19 17:04:57.880010 example: 2019-10-19 17:04:57
description: >- description: >-
The date and time the reading was taken. Make sure The date and time the reading was taken. Make sure
you use the UTC time format. This is because the you use the UTC time format. This is because the
@ -72,11 +72,60 @@ paths:
description: >- description: >-
The reading was successfully added to the database. The reading was successfully added to the database.
/status/update/{device}:
post:
operationId: api.post_a_status_change
tags:
- Log Device Status Change
summary: >-
Logs a change in the status of the specified device.
description: >-
This is mostly to check if the devices included in this project
are turned on, off or an error has occured and they are
unreachable/crashed.
parameters:
- name: device
in: path
description: >-
The Id. of the device with the change in status. 1, 2 and 3
refer to the light-meters in Ritherdon and 4, 5 and 6
refer to the relays in the gallery. The pairing of each
meter and relay is as follows: 1->4, 2->5 and 3->6.
type: integer
required: True
- name: the_status_change
in: body
description: >-
The status change and the time the change occurred.
required: True
schema:
type: object
properties:
time:
type: string
format: date-time
example: 2019-10-19 17:04:57
description: >-
The date and time the change in status occurred. Make
sure to use the U.T.C. time format. This is because
the A.P.I. has made a design decision to standardise
on it.
status:
type: string
example: "on"
description: >-
The current status of the device you would like to log.
The two status types are "on" and "off".
responses:
201:
description: >-
The status update was successfully added to the database.
/readings/latest/{light_meter}: /readings/latest/{light_meter}:
get: get:
operationId: api.get_latest operationId: api.get_latest
tags: tags:
- Request Readings - Request Light-Meter Readings
summary: >- summary: >-
Returns the latest reading from the specified light meter. Returns the latest reading from the specified light meter.
description: >- description: >-
@ -129,7 +178,7 @@ paths:
get: get:
operationId: api.get_all_readings operationId: api.get_all_readings
tags: tags:
- Request Readings - Request Light-Meter Readings
summary: >- summary: >-
Returns every reading taken by the specified light meter. Returns every reading taken by the specified light meter.
description: >- description: >-
@ -180,7 +229,7 @@ paths:
get: get:
operationId: api.get_all_readings_for_every_meter operationId: api.get_all_readings_for_every_meter
tags: tags:
- Request Readings - Request Light-Meter Readings
summary: >- summary: >-
Returns every reading taken by every light meter in the Returns every reading taken by every light meter in the
project. project.