1
0
Fork 0
Browse Source

Merge branch 'unstable' of return-to-ritherdon/midpoint into stable

pull/8/head
Craig Oates 4 years ago committed by Gitea
parent
commit
0bb4ed4be8
  1. 37
      app/api.py
  2. 10
      app/app.py
  3. 35
      app/build_database.py
  4. 82
      app/models/devices.py
  5. BIN
      app/readings.db
  6. 144
      app/services/get_services.py
  7. 90
      app/services/post_services.py
  8. BIN
      app/static/favicons/android-icon-144x144.png
  9. BIN
      app/static/favicons/android-icon-192x192.png
  10. BIN
      app/static/favicons/android-icon-36x36.png
  11. BIN
      app/static/favicons/android-icon-48x48.png
  12. BIN
      app/static/favicons/android-icon-72x72.png
  13. BIN
      app/static/favicons/android-icon-96x96.png
  14. BIN
      app/static/favicons/apple-icon-114x114.png
  15. BIN
      app/static/favicons/apple-icon-120x120.png
  16. BIN
      app/static/favicons/apple-icon-144x144.png
  17. BIN
      app/static/favicons/apple-icon-152x152.png
  18. BIN
      app/static/favicons/apple-icon-180x180.png
  19. BIN
      app/static/favicons/apple-icon-57x57.png
  20. BIN
      app/static/favicons/apple-icon-60x60.png
  21. BIN
      app/static/favicons/apple-icon-72x72.png
  22. BIN
      app/static/favicons/apple-icon-76x76.png
  23. BIN
      app/static/favicons/apple-icon-precomposed.png
  24. BIN
      app/static/favicons/apple-icon.png
  25. 2
      app/static/favicons/browserconfig.xml
  26. BIN
      app/static/favicons/favicon-16x16.png
  27. BIN
      app/static/favicons/favicon-32x32.png
  28. BIN
      app/static/favicons/favicon-96x96.png
  29. BIN
      app/static/favicons/favicon.ico
  30. 41
      app/static/favicons/manifest.json
  31. BIN
      app/static/favicons/ms-icon-144x144.png
  32. BIN
      app/static/favicons/ms-icon-150x150.png
  33. BIN
      app/static/favicons/ms-icon-310x310.png
  34. BIN
      app/static/favicons/ms-icon-70x70.png
  35. BIN
      app/static/images/error.png
  36. BIN
      app/static/images/logo.png
  37. BIN
      app/static/images/off.png
  38. BIN
      app/static/images/on.png
  39. 65
      app/static/styles/main.css
  40. 199
      app/swagger.yml
  41. 18
      app/templates/favicons.html
  42. 79
      app/templates/home.html
  43. 5
      app/templates/robots.txt

37
app/api.py

@ -9,33 +9,26 @@ in the files in the /services/ folder.
These functions are acting as very light controllers essentially.
'''
# The each value represents a light meter.
READINGS = [0, 0, 0]
def post_a_reading(light_meter, the_reading):
if light_meter == 1:
READINGS[0] = the_reading.get("reading")
elif light_meter == 2:
READINGS[1] = the_reading.get("reading")
elif light_meter == 3:
READINGS[2] = the_reading.get("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):
# YOU ARE UP TO HERE. NEED TO REFACTOR BACK INTO DATABASE FUNCTION
# BELOW THIS FUNCTION. YAML FILE NEEDS CLEANING UP TOO.
if light_meter == 1:
return READINGS[0]
elif light_meter == 2:
return READINGS[1]
elif light_meter == 3:
return READINGS[2]
def get_latest_reading(light_meter):
return get_services.get_in_mem_reading(light_meter)
return get_services.get_latest_reading(light_meter)
def get_all_readings(light_meter):
return get_services.get_all_readings_from_table(light_meter)
def get_all_readings_for_every_meter():
return get_services.get_all_readings_from_database()
def get_latest_device_status(device):
return get_services.get_latest_status(device)
def get_all_status_log(device):
return get_services.get_all_status_logs_from_table(device)
def get_all_status_logs_for_every_device():
return get_services.get_all_status_changes_from_database()

10
app/app.py

@ -1,6 +1,7 @@
from flask import Flask, render_template
import connexion
import config
from services import get_services
# The application instance.
app = config.connex_app
@ -10,7 +11,14 @@ app.add_api("swagger.yml")
@app.route("/")
def home ():
return render_template("home.html")
data = get_services.get_latest_status_for_all_devices()
print(data)
return render_template("home.html", data=data)
@app.route("/robots.txt")
@app.route("/robots")
def robots():
return render_template("robots.txt")
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)

35
app/build_database.py

@ -2,6 +2,7 @@ import os
from datetime import datetime
from config import db
from models.meters import Meter1, Meter2, Meter3
from models.devices import Device1, Device2, Device3, Device4, Device5, Device6
def get_timestamp():
return datetime.now().strftime(("%Y-%m-%d %H:%M:%S"))
@ -10,6 +11,12 @@ def get_timestamp():
READINGS1 =[ {"time":datetime.now(), "reading": 0} ]
READINGS2 =[ {"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
if os.path.exists("readings.db"):
@ -28,9 +35,35 @@ for info in READINGS2:
r = Meter2(time=info.get("time"), reading=info.get("reading"))
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:
r = Meter3(time=info.get("time"), reading=info.get("reading"))
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()

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 = Device3
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 = Device4
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 = Device5
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 = Device6
sqla_session = db.session

BIN
app/readings.db

Binary file not shown.

144
app/services/get_services.py

@ -2,6 +2,9 @@ from flask import make_response, abort
from config import db
from models.meters import (Meter1, Meter1Schema, Meter2, Meter2Schema,
Meter3, Meter3Schema)
from models.devices import (Device1, Device1Schema, Device2, Device2Schema,
Device3, Device3Schema, Device4, Device4Schema,
Device5, Device5Schema, Device6, Device6Schema)
'''
Get Services Note
@ -12,15 +15,17 @@ at Ritherdon, you will need to head to the /post_services.py/ file.
It should be in the same directory at this: /services/.
'''
def get_in_mem_reading(meter):
# Need to refactor in use an in-mem database.
# Currently grabbing from database.
bad_meter_id_message = "Meter Id. not recognised. Must be between 1 and 3."
bad_device_id_message = "Device Id. not recognised. Must be between 1 and 6."
def get_latest_reading(meter):
if meter == 1:
return get_m1_latest()
elif meter == 2:
return get_m2_latest()
elif meter == 3:
return get_m3_latest()
return make_response(bad_meter_id_message, 400)
def get_all_readings_from_table(name):
if name == 1:
@ -29,18 +34,55 @@ def get_all_readings_from_table(name):
return get_all_readings_for_meter2()
elif name == 3:
return get_all_readings_for_meter3()
return make_response(bad_meter_id_message, 400)
def get_all_readings_from_database():
return get_all_readings()
def get_latest_status(device):
if device == 1:
return get_d1_latest()
elif device == 2:
return get_d2_latest()
elif device == 3:
return get_d3_latest()
elif device == 4:
return get_d4_latest()
elif device == 5:
return get_d5_latest()
elif device == 6:
return get_d6_latest()
return make_response(bad_device_id_message, 400)
def get_all_status_logs_from_table(device):
if device == 1:
return get_all_status_logs_for_d1()
elif device == 2:
return get_all_status_logs_for_d2()
elif device == 3:
return get_all_status_logs_for_d3()
elif device == 4:
return get_all_status_logs_for_d4()
elif device == 5:
return get_all_status_logs_for_d5()
elif device == 6:
return get_all_status_logs_for_d6()
return make_response(bad_device_id_message, 400)
def get_all_status_changes_from_database():
return get_all_status_logs()
def get_latest_status_for_all_devices():
return get_all_latest_logs()
'''
The Nitty-Gritty Functions
======================================================================
The functions below are the main functions within this file. The files
above act as "header functions" for the methods in /api.py/. I find it
easier to see what the method names are when this file and /api.py/
are open side-by-side. At the very least it reduces the amount I need
to scroll up and down the file to find what I am after.
above act as "header functions" to be called by the functions in /api.py/.
I find it easier to see what the method names are when this file and
/api.py/ are open side-by-side. At the very least it reduces the amount
I need to scroll up and down the file to find what I am after.
'''
def get_m1_latest():
@ -82,3 +124,91 @@ def get_all_readings():
m3 = get_all_readings_for_meter3()
readings = {"meter1": m1, "meter2": m2, "meter3": m3}
return readings
def get_d1_latest():
status = Device1.query.order_by(Device1.id.desc()).first()
device_schema = Device1Schema()
return device_schema.dump(status)
def get_d2_latest():
status = Device2.query.order_by(Device2.id.desc()).first()
device_schema = Device2Schema()
return device_schema.dump(status)
def get_d3_latest():
status = Device3.query.order_by(Device3.id.desc()).first()
device_schema = Device3Schema()
return device_schema.dump(status)
def get_d4_latest():
status = Device4.query.order_by(Device4.id.desc()).first()
device_schema = Device4Schema()
return device_schema.dump(status)
def get_d5_latest():
status = Device5.query.order_by(Device5.id.desc()).first()
device_schema = Device5Schema()
return device_schema.dump(status)
def get_d6_latest():
status = Device6.query.order_by(Device6.id.desc()).first()
device_schema = Device6Schema()
return device_schema.dump(status)
def get_all_status_logs_for_d1():
status_logs = Device1.query.order_by(Device1.id.desc()).all()
schema = Device1Schema(many=True)
data = schema.dump(status_logs)
return data
def get_all_status_logs_for_d2():
status_logs = Device2.query.order_by(Device2.id.desc()).all()
schema = Device2Schema(many=True)
data = schema.dump(status_logs)
return data
def get_all_status_logs_for_d3():
status_logs = Device3.query.order_by(Device3.id.desc()).all()
schema = Device3Schema(many=True)
data = schema.dump(status_logs)
return data
def get_all_status_logs_for_d4():
status_logs = Device4.query.order_by(Device4.id.desc()).all()
schema = Device4Schema(many=True)
data = schema.dump(status_logs)
return data
def get_all_status_logs_for_d5():
status_logs = Device5.query.order_by(Device5.id.desc()).all()
schema = Device5Schema(many=True)
data = schema.dump(status_logs)
return data
def get_all_status_logs_for_d6():
status_logs = Device6.query.order_by(Device6.id.desc()).all()
schema = Device6Schema(many=True)
data = schema.dump(status_logs)
return data
def get_all_status_changes_from_database():
d1 = get_all_status_logs_for_d1()
d2 = get_all_status_logs_for_d2()
d3 = get_all_status_logs_for_d3()
d4 = get_all_status_logs_for_d4()
d5 = get_all_status_logs_for_d5()
d6 = get_all_status_logs_for_d6()
logs = {"device 1": d1, "device 2": d2, "device 3": d3,
"device 4": d4, "device 5": d5, "device 6": d6}
return logs
def get_all_latest_logs():
d1 = get_d1_latest()
d2 = get_d2_latest()
d3 = get_d3_latest()
d4 = get_d4_latest()
d5 = get_d5_latest()
d6 = get_d6_latest()
logs = {"device 1": d1, "device 2": d2, "device 3": d3,
"device 4": d4, "device 5": d5, "device 6": d6}
return logs

90
app/services/post_services.py

@ -2,14 +2,18 @@ from flask import make_response, abort
from config import db
from models.meters import (Meter1, Meter1Schema, Meter2, Meter2Schema,
Meter3, Meter3Schema)
from models.devices import (Device1, Device1Schema, Device2, Device2Schema,
Device3, Device3Schema, Device4, Device4Schema,
Device5, Device5Schema, Device6, Device6Schema)
'''
Post Services Note
======================================================================
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
the (this) server, you will need to head to the /get_services.py/ file.
It should be in the same directory as this: /services/.
light meters in Ritherdon, and log changes in state for any of the
devices. It you are wanting to retrieve data from the (this) server,
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):
@ -19,24 +23,96 @@ def add_latest_reading(meter,reading):
return add_reading_to_meter2(reading)
elif meter == 3:
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):
schema = Meter1Schema()
new_reading = schema.load(the_reading, session=db.session)
db.session.add(new_reading)
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):
schema = Meter2Schema()
new_reading = schema.load(the_reading, session=db.session)
db.session.add(new_reading)
db.session.commit()
return make_response("Reading successfully stored database.", 201)
return make_response(reading_message, 201)
def add_reading_to_meter3(the_reading):
schema = Meter3Schema()
new_reading = schema.load(the_reading, session=db.session)
db.session.add(new_reading)
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)

BIN
app/static/favicons/android-icon-144x144.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
app/static/favicons/android-icon-192x192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
app/static/favicons/android-icon-36x36.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
app/static/favicons/android-icon-48x48.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
app/static/favicons/android-icon-72x72.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
app/static/favicons/android-icon-96x96.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
app/static/favicons/apple-icon-114x114.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
app/static/favicons/apple-icon-120x120.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
app/static/favicons/apple-icon-144x144.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
app/static/favicons/apple-icon-152x152.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
app/static/favicons/apple-icon-180x180.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
app/static/favicons/apple-icon-57x57.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
app/static/favicons/apple-icon-60x60.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
app/static/favicons/apple-icon-72x72.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
app/static/favicons/apple-icon-76x76.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
app/static/favicons/apple-icon-precomposed.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
app/static/favicons/apple-icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

2
app/static/favicons/browserconfig.xml

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>

BIN
app/static/favicons/favicon-16x16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
app/static/favicons/favicon-32x32.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
app/static/favicons/favicon-96x96.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
app/static/favicons/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

41
app/static/favicons/manifest.json

@ -0,0 +1,41 @@
{
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

BIN
app/static/favicons/ms-icon-144x144.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
app/static/favicons/ms-icon-150x150.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
app/static/favicons/ms-icon-310x310.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
app/static/favicons/ms-icon-70x70.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
app/static/images/error.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
app/static/images/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
app/static/images/off.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
app/static/images/on.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

65
app/static/styles/main.css

@ -1,3 +1,66 @@
html {
font-size: 12px;
font-family: arial, sans-serif;
background: #f5f5f5;
color: #424242;
}
main {
display: flex;
justify-content: center;
padding: 20px;
}
main div {
max-width: 800px;
left-margin: auto;
right-margin: auto;
}
main div .header {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
main div .header img {
width: 50px;
}
main div .header h1 {
text-align: center;
font-size: 20px;
}
main div .status-bar {
display: flex;
margin: 5px;
flex-wrap: wrap;
justify-content: space-evenly;
padding: 20px;
border-top: 2px solid #424242;
border-bottom: 2px solid #424242;
}
.meter {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
main div .status-bar .meter img {
width: 30px;
height: 30px;
}
.meter p {
padding: 0px;
margin: 0px;
}
main div h2 {
font-size: 16px;
}

199
app/swagger.yml

@ -2,7 +2,7 @@ swagger: "2.0"
info:
description: >-
The 'Return to Ritherdon' project, by Nicola Ellis, is a two year art residency at Ritherdon & Co Ltd, a manufacturer of metal enclosures based in Darwen, Lancashire U.K. This website is part of the many outcomes produced throughout the duration of project.
version: "0.1.0 - Alpha"
version: "1.0.0 - Alpha"
title: Return to Ritherdon Project A.P.I.
consumes:
- application/json
@ -12,11 +12,13 @@ produces:
basePath: /api
paths:
# Light-Meter A.P.I. Calls
# ====================================================================
/readings/add/{light_meter}:
post:
operationId: api.post_a_reading
tags:
- Store Readings
- Log Light-Meter Readings
summary: >-
Posts a reading from one of the project's light meters and store
it in the database.
@ -54,7 +56,7 @@ paths:
time:
type: string
format: date-time
example: 2019-10-19 17:04:57.880010
example: 2019-10-19 17:04:57
description: >-
The date and time the reading was taken. Make sure
you use the UTC time format. This is because the
@ -74,9 +76,9 @@ paths:
/readings/latest/{light_meter}:
get:
operationId: api.get_latest_reading
operationId: api.get_latest
tags:
- Request Readings
- Request Light-Meter Readings
summary: >-
Returns the latest reading from the specified light meter.
description: >-
@ -97,7 +99,7 @@ paths:
200:
description: >-
If the server can successfully retrieve the latest reading
for the specified light meter, you should receive a JASON
for the specified light meter, you should receive a JSON
object like the one below. It should include the Id. of
the light meter it was taken with, the time the reading
was taken and the reading itself.
@ -129,11 +131,11 @@ paths:
get:
operationId: api.get_all_readings
tags:
- Request Readings
- Request Light-Meter Readings
summary: >-
Returns every reading taken by the specified light meter.
description: >-
Use this U.R.L. to retrieve the all the reading from the
Use this U.R.L. to retrieve all the reading from the
light meter you specified (in the U.R.L.). At the time of
writing, the project has only three light meters and are
labelled 1-3.
@ -180,7 +182,7 @@ paths:
get:
operationId: api.get_all_readings_for_every_meter
tags:
- Request Readings
- Request Light-Meter Readings
summary: >-
Returns every reading taken by every light meter in the
project.
@ -201,36 +203,179 @@ paths:
type: object
additionalProperties: True
# This is the in-mem. database URL.
/readings/late/{light_meter}:
# Device A.P.I. Calls
# ====================================================================
/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.
/status/latest/{device}:
get:
operationId: api.get_latest
operationId: api.get_latest_device_status
tags:
- Request Readings
- Request Device Status
summary: >-
Returns latest from in-mem database.
Returns the current status of the specified device.
description: >-
latest in-mem db reading
Use this U.R.L. to retrieve the current status of the device
you specified (in the U.R.L.). At the time of writing, the
project has six devices in total which are split equally
between Ritherdon and the gallery. Devices 1 to 3 (light-meters)
refer to the devices in Ritherdon and 4 to 6 (relays) are in
the gallery. Each light-meter and relay should pair up in the
following ways: 1->4, 2->5 and 3->6.
parameters:
- name: light_meter
- name: device
in: path
description: >-
This is the Id. of the light meter which you are retrieving
the readings for. The Id. consists of a number between 1-3
at the time of writing.
This is the Id. of the device which you are retrieving the
current status for. The Id. should consist of a number
between 1 and 6, at the time of writing.
type: integer
required: True
responses:
200:
description: >-
In-mem db latest value
If the server can successfully retrieve the current status
for the specified device, you should receive a JSON object
like the one below. It should include the Id. of the
device, the current status and the time it was logged.
schema:
type: object
properties:
reading:
type: integer
example: 34
description: >-
This is the actual reading taken from the specified
light meter, at the time specified in this response
body (I.E. JSON object).
id:
type: integer
example: 2
description: >-
The is the Id. of the device.
time:
type: string
example: 2019-10-19 17:04:57
description: >-
The time the status change was logged in the
database. The A.P.I. has standardised on the
U.T.C. format.
status:
type: string
example: on
description: The current status of the device.
/status/all/{device}:
get:
operationId: api.get_all_status_log
tags:
- Request Device Status
summary: >-
Returns every status changed logged by the specified device.
description: >-
Use this U.R.L. to retrieve every change in the specified
devices state. At the time of writing, the project has six
devices and are labelled 1 to 6. Devices (light-meters) 1 to 3
are located in the Ritherdon factory and 4 to 6 (relays) in the
gallery. The light-meters and relays are paired like so, 1->4,
2->5 and 3->6.
parameters:
- name: device
in: path
description: >-
The Id. of the device you are retrieving the status change
log for. The Id. should be a number between 1 and 6.
type: integer
required: True
responses:
200:
description: >-
If the server can successfully retrieve all the status logs
for the specified device, you should receive an array of
JSON objects like the one in the example below. It should
include the database Id. of the logged status change, the
time the status change was logged and the status itself.
schema:
type: object
properties:
id:
type: integer
example: 3
description: >-
This is the database Id. of the logged status change.
time:
type: string
example: 2019-10-19 17:23:23
description: >-
The time and date the status change was logged. The
A.P.I. has standardised on the U.T.C. format.
status:
type: string
example: off
description: >-
The status of the device after the change occurred.
/status/all:
get:
operationId: api.get_all_status_logs_for_every_device
tags:
- Request Device Status
summary: >-
Returns every logged status change of every device in the
project.
description: >-
Use this U.R.L. to retrieve every logged status change of every
device used in this project. There is no example of the data
returned because I can't seem to replicate it as a YAML schema
which is understood by Swagger. Because of this. the return
type is registered as a free-form object. To see what the data
looks like when it is returned, I recommend you use the "Try it
out!" button to see a working example.
responses:
200:
description: >-
All the logged status changes of every device were
successfully retrieved from the database and delivered.
schema:
type: object
additionalProperties: True

18
app/templates/favicons.html

@ -1 +1,17 @@
<!-- need to add favicons at some point... -->
<link rel="apple-touch-icon" sizes="57x57" href="{{ url_for('static', filename='favicons/apple-icon-57x57.png') }}">
<link rel="apple-touch-icon" sizes="60x60" href="{{ url_for('static', filename='favicons/apple-icon-60x60.png') }}">
<link rel="apple-touch-icon" sizes="72x72" href="{{ url_for('static', filename='favicons/apple-icon-72x72.png') }}">
<link rel="apple-touch-icon" sizes="76x76" href="{{ url_for('static', filename='favicons/apple-icon-76x76.png') }}">
<link rel="apple-touch-icon" sizes="114x114" href="{{ url_for('static', filename='favicons/apple-icon-114x114.png') }}">
<link rel="apple-touch-icon" sizes="120x120" href="{{ url_for('static', filename='favicons/apple-icon-120x120.png') }}">
<link rel="apple-touch-icon" sizes="144x144" href="{{ url_for('static', filename='favicons/apple-icon-144x144.png') }}">
<link rel="apple-touch-icon" sizes="152x152" href="{{ url_for('static', filename='favicons/apple-icon-152x152.png') }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ url_for('static', filename='favicons/apple-icon-180x180.png') }}">
<link rel="icon" type="image/png" sizes="192x192" href="{{ url_for('static', filename='favicons/android-icon-192x192.png') }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ url_for('static', filename='favicons/favicon-32x32.png') }}">
<link rel="icon" type="image/png" sizes="96x96" href="{{ url_for('static', filename='favicons/favicon-96x96.png') }}">
<link rel="icon" type="image/png" sizes="16x16" href="{{ url_for('static', filename='favicons/favicon-16x16.png') }}">
<link rel="manifest" href="{{ url_for('static', filename='favicons/manifest.json') }}">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="{{ url_for('static', filename='favicons/ms-icon-144x144.png') }}">
<meta name="theme-color" content="#ffffff">

79
app/templates/home.html

@ -1,4 +1,81 @@
{% extends "layout.html" %}
{% block content %}
<h1>Return to Ritherdon Project</h1>
<main>
<div>
<div class="header">
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="logo"/>
<h1>Return to Ritherdon Project</h1>
</div>
<h2>Artwork Status</h2>
<div class="status-bar">
<div class="meter">
<img src="{{ url_for('static', filename='images/%s.png' % data["device 1"].status) }}" alt="logo"/>
<p>Factory 1</p>
</div>
<div class="meter">
<img src="{{ url_for('static', filename='images/%s.png' % data["device 2"].status) }}" alt="logo"/>
<p>Factory 2</p>
</div>
<div class="meter">
<img src="{{ url_for('static', filename='images/%s.png' % data["device 3"].status) }}" alt="logo"/>
<p>Factory 3</p>
</div>
<div class="meter">
<img src="{{ url_for('static', filename='images/%s.png' % data["device 4"].status) }}" alt="logo"/>
<p>Gallery 1</p>
</div>
<div class="meter">
<img src="{{ url_for('static', filename='images/%s.png' % data["device 5"].status) }}" alt="logo"/>
<p>Gallery 2</p>
</div>
<div class="meter">
<img src="{{ url_for('static', filename='images/%s.png' % data["device 6"].status) }}" alt="logo"/>
<p>Gallery 3</p>
</div>
</div>
<h2>About the Project</h2>
<p>
"Return to Ritherdon" is a two year residency at Ritherdon & Co Ltd, a manufacturer of metal enclosures based in Darwen, Lancashire U.K.
</p>
<h2>About the Artist: Nicola Ellis</h2>
<p>Nicola is interested in the properties, value, function and circulation of materials. She has a current focus on metals and the companies that work with them, her work draws on the visual and spoken language of industry operations, fabrication and profiling processes. The parameters for her sculpture, installation, drawings and videos include relationships between people, businesses and technology.
</p>
<h2>About the Gallery: Castlefield Gallery</h2>
<p>
Established by artists in 1984, Castlefield Gallery is the first public contemporary visual art gallery to have opened in Manchester. The arrival of Castlefield Gallery in the city was a significant catalyst of what became a period of rapid cultural growth, and the increase of the presence of visual artists in the city and city region. As a registered charity, our mission drives our work to focus on artistic and career development for artists, as well as deepening audiences’ relationship to contemporary art. Over the last three decades we have become an integral part of the North of England’s cultural fabric and a vital support to artists across the North West and further afield.
</p>
<h2>About this Website</h2>
<p>
This website is part of an, unnamed, artwork which is part of the "Return to Ritherdon" project.
</p>
<h2>Links</h2>
<ul>
<li><a href="http://www.nicolaellis.com/" title="Click this to visit Nicola's personal website.">Nicola's Personal Website</a></li>
<li><a href="https://www.castlefieldgallery.co.uk/" title="Click this to visit Castlefield's website.">Castlefield's Website</a></li>
<li><a href="{{ url_for('/api./api_swagger_ui_index') }}" title="Click this to inspect this site's A.P.I.">Swagger A.P.I.</a></li>
</ul>
</div>
</main>
{% endblock content %}

5
app/templates/robots.txt

@ -0,0 +1,5 @@
# www.robotstxt.org/
# Disallow The Wayback Machine
User-agent: ia_archiver
Disallow:/