Skip to content

Commit

Permalink
initial very basic working Flask app and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jantman committed Jun 13, 2024
1 parent d492336 commit e7da56d
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 17 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ repos:
entry: check-yaml
language: system
types: [yaml]
exclude: ^esphome-config\.yaml$
- id: darglint
name: darglint
entry: darglint
Expand Down
14 changes: 14 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ Finally, install and set up pre-commit:
$ nox --session=pre-commit -- install
```

## Running Locally

```console
$ flask --app dm_mac run
```

The app will now be available at [http://127.0.0.1:5000](http://127.0.0.1:5000)

## How to test the project

Run the full test suite:
Expand All @@ -54,6 +62,12 @@ For example, invoke the unit test suite like this:
$ nox --session=tests
```

To manually run the pre-commit tests:

```console
$ nox --session=pre-commit
```

Unit tests are located in the [tests/](tests/) directory,
and are written using the [pytest](https://pytest.readthedocs.io/) testing framework.

Expand Down
28 changes: 14 additions & 14 deletions notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ In terms of hardware, what I think makes the most sense is:
1. A main box that mounts near the user controls, with the ESP32, RFID reader, display, and LED. This is a standardized 3d-printed box design with the UI and RFID components on the front, a board inside, a connector on the back, and design provision for mounting with screws and a custom bracket on any side. The box halves would be secured with tamper-proof screws and an appropriate warning label over at least one screw.
2. A “power” box that would contain just:
1. 120VAC (or 240VAC for special cases) to 5VDC power supply
2. control relay*
3. current sensor*
2. control relay\*
3. current sensor\*
3. A 6-conductor cable connecting the two, likely shielded, using locking aircraft-style connectors

- The normal case we assume is a 120VAC power tool that’s essentially just a motor, where we want to control power to it and see if it’s running. For special cases, like the laser or other things that use lower-voltage (or at least lower-amperage) control circuits, we’d develop custom power boxes. It’s a simple enough interface - we have 2 wires for a relay control output from the ESP32 (which could be as simple as ground and a direct connection to an ESP32 pin, or could have an at-least-minimal relay in between), 2 wires that provide +5VDC and GND to the ESP32 and associated components in the control box, and 2 optional wires that provide output from a current clamp on the machine power input or some other method of sensing machine state.
Expand All @@ -48,9 +48,9 @@ Additional thoughts:

The server API should be dead-simple:

* On an event (card scan or removal or button press), POST to the server containing the event type, information (card ID) if applicable, machine name, current sensor value if applicable, and a shared secret string.
* Aside from button presses or card changes, we’ll also have a timer event every X seconds that just updates the current sensor value, if applicable.
* Server responds with a simple JSON data structure containing what should be displayed on screen (written to global variable and printed to screen), desired state of the relay, and desired RGB LED state and/or Oops button LED state.
- On an event (card scan or removal or button press), POST to the server containing the event type, information (card ID) if applicable, machine name, current sensor value if applicable, and a shared secret string.
- Aside from button presses or card changes, we’ll also have a timer event every X seconds that just updates the current sensor value, if applicable.
- Server responds with a simple JSON data structure containing what should be displayed on screen (written to global variable and printed to screen), desired state of the relay, and desired RGB LED state and/or Oops button LED state.

I would like the server to run on-premise within the building… (1) we want equipment authentication and logging to continue working even if the Internet is down, and (2) if the power is out this doesn’t matter anyway, so that’s not a concern.

Expand All @@ -60,12 +60,12 @@ Server to machine reply: desired relay state, LCD message, LCD backlight state,

Machine state representation, in database:

* machine name
* machine current IP
* relay state
* relay last state change time
* rfid_card_number (nullable)
* rfid card last state change time
* oopsed_since (nullable)
* current_draw (nullable; maybe integer? or maybe machine_is_on bool?)
* last_changed
- machine name
- machine current IP
- relay state
- relay last state change time
- rfid_card_number (nullable)
- rfid card last state change time
- oopsed_since (nullable)
- current_draw (nullable; maybe integer? or maybe machine_is_on bool?)
- last_changed
1 change: 1 addition & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def tests(session: Session) -> None:
"-m",
"pytest",
"--blockage",
"-v",
*session.posargs,
)
finally:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ tests = ["tests", "*/tests"]

[tool.coverage.run]
branch = true
source = ["dm_mac", "tests"]
source = ["dm_mac"]

[tool.coverage.report]
show_missing = true
Expand Down
15 changes: 14 additions & 1 deletion src/dm_mac/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
"""Machine_Access_Control."""
"""Decatur Makers Machine Access Control."""

from flask import Flask


def create_app() -> Flask:
"""Factory to create the app."""
app: Flask = Flask("dm_mac")

from dm_mac.views.api import api

app.register_blueprint(api)

return app
1 change: 1 addition & 0 deletions src/dm_mac/views/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Init for views (empty)."""
12 changes: 12 additions & 0 deletions src/dm_mac/views/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""API Views."""

from flask import Blueprint


api: Blueprint = Blueprint("api", __name__, url_prefix="/api")


@api.route("/")
def index():
"""Main API index route - placeholder."""
return "Hello, World!"
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"""Test suite for the dm_mac package."""
"""Test suite for the dm_mac package (empty)."""
30 changes: 30 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Conftest for dm_mac - fixtures."""

import pytest
from flask import Flask
from flask.testing import FlaskClient

from dm_mac import create_app


@pytest.fixture()
def app() -> Flask:
"""Test App fixture - app instance configured for testing."""
app = create_app()
app.config.update(
{
"TESTING": True,
}
)

# other setup can go here

yield app

# clean up / reset resources here


@pytest.fixture()
def client(app: Flask) -> FlaskClient:
"""Test Client for making requests to test app."""
return app.test_client()
1 change: 1 addition & 0 deletions tests/views/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Test views init (empty)."""
15 changes: 15 additions & 0 deletions tests/views/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""Tests for API Views."""

from flask.testing import FlaskClient
from werkzeug.test import TestResponse


class TestIndex:
"""Tests for API Index view."""

def test_index_response(self, client: FlaskClient):
"""Test for API index response."""
response: TestResponse = client.get("/api/")
assert response.status_code == 200
assert response.text == "Hello, World!"
assert response.headers["Content-Type"] == "text/html; charset=utf-8"

0 comments on commit e7da56d

Please sign in to comment.