Skip to content

Commit

Permalink
Refactor: Use pdm and ruff. Rename "test" to "tests". Introduce semver
Browse files Browse the repository at this point in the history
  • Loading branch information
hackenbergstefan committed Jul 24, 2024
1 parent 360017a commit 82ffb9c
Show file tree
Hide file tree
Showing 37 changed files with 1,093 additions and 167 deletions.
4 changes: 0 additions & 4 deletions .flake8

This file was deleted.

16 changes: 7 additions & 9 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Format with black
- name: Install dependencies
run: |
pip install black
black --check .
- name: Lint with flake8
pip install --user pdm
pdm sync -G dev
- name: Format
run: |
pip install flake8
flake8 --extend-ignore=W503
- name: Lint with pylint
pdm run ruff format --check .
- name: Lint
run: |
pip install pylint
pylint coffeebuddy
pdm run ruff --check .
7 changes: 3 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get install libpcsclite-dev
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest
pip install --user pdm
pdm sync -G dev
- name: Test with pytest
run: |
pytest -v test/*
pdm run pytest -v
10 changes: 5 additions & 5 deletions .github/workflows/test_backends.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get install libpcsclite-dev
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install --user pdm
pdm sync
- name: Test backend sqlite
run: |
sed -i 's/^CARD.*/CARD = ""/' config.py
sed -i 's/^FACERECOGNITION.*/FACERECOGNITION = False/' config.py
sed -i 's/^PIR.*/PIR = False/' config.py
sed -i 's/^ILLUMINATION.*/ILLUMINATION = False/' config.py
sed -i 's/^DB_BACKEND.*/DB_BACKEND = "sqlite"/' config.py
./bin/run.py &
pdm run ./bin/run.py &
pid=$!
sleep 1
ps | grep -c $pid || wait $pid
Expand All @@ -36,7 +36,7 @@ jobs:
sed -i 's/^PIR.*/PIR = False/' config.py
sed -i 's/^ILLUMINATION.*/ILLUMINATION = False/' config.py
sed -i 's/^DB_BACKEND.*/DB_BACKEND = "postgres"/' config.py
./bin/run.py &
pdm run ./bin/run.py &
pid=$!
sleep 1
ps | grep -c $pid || wait $pid
Expand All @@ -50,7 +50,7 @@ jobs:
sed -i 's/^DB_BACKEND.*/DB_BACKEND = "sqlite"/' config.py
export FLASK_ENV=prefilled
export FLASK_DEBUG=true
./bin/run.py &
pdm run ./bin/run.py &
pid=$!
sleep 1
ps | grep -c $pid || wait $pid
Expand Down
35 changes: 27 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,43 @@ Do you use a paper based tally sheet to count your team's coffee consumption? Th

## Usage

1. Optional: Create virtual environment
```bash
python3 -m venv .env
. .env/bin/activate
pip install -r requirements.txt
Coffebuddy uses [pdm](https://pdm-project.org/en/latest/) to manage its python dependencies.

1. Install dependencies

```sh
pdm sync
```

If `pyscard` fails building you might need to install dependencies. For Debian based distributions this would be

```sh
sudo apt install swig libpcsclite-dev
```
2. Connect a pcsc smart card reader. I use a [uTrust 4701f](https://support.identiv.com/4701f/). Drivers for Ubuntu can be installed for example by

2. Connect a pcsc smart card reader.
I use a [uTrust 4701f](https://support.identiv.com/4701f/).
Drivers for Ubuntu can be installed for example by

```sh
sudo apt install pcscd pcsc-tools
```

3. Start `production` environment

```sh
./bin/run.py
```

or `development` environment

```sh
FLASK_ENV=development ./bin/run.py
```

## Tests
Run tests with `python -m unittest test/test_app.py`

Run tests with `pytest -v tests`


## Application
Expand All @@ -45,13 +57,16 @@ The final application uses a Raspberry Pi attached to a 7" touchscreen. Thus, th
At least I had to adjust the following settings:
* Fix screen resolution
```conf
hdmi_group=2
hdmi_mode=87
hdmi_cvt 1024 600 60 3 0 0 1
hdmi_drive=2
```
* If display has to be rotated by 180° adjust `/etc/X11/xorg.conf.d/40-libinput.conf`
```conf
Section "InputClass"
Identifier "libinput touchscreen catchall"
Expand All @@ -61,10 +76,14 @@ At least I had to adjust the following settings:
Option "CalibrationMatrix" "-1 0 1 0 -1 1 0 0 1"
EndSection
```
* Disable translation option in chrome
#### Card reader
Coffeebuddy works with PCSC reader and with SPI RFID module "RC522". Latter is supported on Raspi by several python modules. Although [mrfc522](https://github.com/pimylifeup/MFRC522-python) is widely used it leads to a high CPU consumption when polling for card. [pi-rc522](https://github.com/ondryaso/pi-rc522) uses interrupt based SPI communication.
Coffeebuddy works with PCSC reader and with SPI RFID module "RC522".
Latter is supported on Raspi by several python modules.
Although [mrfc522](https://github.com/pimylifeup/MFRC522-python) is widely used it leads to a high CPU consumption when polling for card.
[pi-rc522](https://github.com/ondryaso/pi-rc522) uses interrupt based SPI communication.
Both modules can be used and selected in [config.py](./config.py).
8 changes: 4 additions & 4 deletions bin/autoonoff.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env python
import RPi.GPIO as GPIO
import subprocess
import time

from RPi import GPIO

SENSOR_PIN = 14
TIME_ON = 20
Expand All @@ -11,12 +11,12 @@
def main():
GPIO.setmode(GPIO.BCM)
GPIO.setup(SENSOR_PIN, GPIO.IN)
subprocess.run(["xset", "dpms", "force", "off"])
subprocess.run(["xset", "dpms", "force", "off"], check=False)

def callback(_):
subprocess.run(["xset", "dpms", "force", "on"])
subprocess.run(["xset", "dpms", "force", "on"], check=False)
time.sleep(TIME_ON)
subprocess.run(["xset", "dpms", "force", "off"])
subprocess.run(["xset", "dpms", "force", "off"], check=False)

try:
GPIO.add_event_detect(SENSOR_PIN, GPIO.RISING, callback=callback)
Expand Down
2 changes: 1 addition & 1 deletion bin/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import coffeebuddy # noqa: E402

try:
import RPi.GPIO as GPIO
from RPi import GPIO

GPIO.setmode(GPIO.BCM)
except ModuleNotFoundError:
Expand Down
72 changes: 58 additions & 14 deletions coffeebuddy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import datetime
import dataclasses
import datetime
import logging
import os
import random
Expand All @@ -13,7 +13,6 @@
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.exc import OperationalError


db = SQLAlchemy()
login_manager = flask_login.LoginManager()

Expand All @@ -36,7 +35,9 @@ def create_app(config=None):
app.socketio = SocketIO(app)

if os.path.exists(f"config_{socket.gethostname()}.py"):
logging.getLogger(__name__).info(f'Using config file "config_{socket.gethostname()}"')
logging.getLogger(__name__).info(
f'Using config file "config_{socket.gethostname()}"'
)
app.config.from_object(f"config_{socket.gethostname()}")
else:
logging.getLogger(__name__).info('Using config file "config"')
Expand All @@ -63,7 +64,10 @@ def init_db(app):
flask.current_app.db.init_app(app)

if (
(flask.current_app.config["DB_BACKEND"] == "sqlite" and not os.path.exists("coffee.db"))
(
flask.current_app.config["DB_BACKEND"] == "sqlite"
and not os.path.exists("coffee.db")
)
or flask.current_app.config.get("ENV") in ("development", "prefilled")
or flask.current_app.testing
):
Expand All @@ -77,7 +81,10 @@ def init_db(app):
if flask.current_app.config.get("ENV") == "development":
flask.current_app.db.session.add(
coffeebuddy.model.User(
tag=bytes.fromhex("01020304"), name="Mustermann", prename="Max", email="[email protected]"
tag=bytes.fromhex("01020304"),
name="Mustermann",
prename="Max",
email="[email protected]",
)
)
flask.current_app.db.session.commit()
Expand All @@ -86,8 +93,14 @@ def init_db(app):
prefill()

if flask.current_app.config["GUEST"]:
if not coffeebuddy.model.User.query.filter(coffeebuddy.model.User.name == "Guest").first():
flask.current_app.db.session.add(coffeebuddy.model.User(tag=b"\xff\xff\xff\xff", name="Guest", prename=""))
if not coffeebuddy.model.User.query.filter(
coffeebuddy.model.User.name == "Guest"
).first():
flask.current_app.db.session.add(
coffeebuddy.model.User(
tag=b"\xff\xff\xff\xff", name="Guest", prename=""
)
)
flask.current_app.db.session.commit()

return flask.current_app.db
Expand Down Expand Up @@ -129,12 +142,42 @@ def prefill():
import coffeebuddy.model

demousers = [
{"prename": "Donald", "postname": "Duck", "email": "[email protected]", "oneswipe": True},
{"prename": "Dagobert", "postname": "Duck", "email": "[email protected]", "oneswipe": False},
{"prename": "Gyro", "postname": " Gearloose", "email": "[email protected]", "oneswipe": False},
{"prename": "Tick ", "postname": "Duck", "email": "[email protected]", "oneswipe": False},
{"prename": "Trick", "postname": "Duck", "email": "[email protected]", "oneswipe": False},
{"prename": "Truck", "postname": "Duck", "email": "[email protected]", "oneswipe": False},
{
"prename": "Donald",
"postname": "Duck",
"email": "[email protected]",
"oneswipe": True,
},
{
"prename": "Dagobert",
"postname": "Duck",
"email": "[email protected]",
"oneswipe": False,
},
{
"prename": "Gyro",
"postname": " Gearloose",
"email": "[email protected]",
"oneswipe": False,
},
{
"prename": "Tick ",
"postname": "Duck",
"email": "[email protected]",
"oneswipe": False,
},
{
"prename": "Trick",
"postname": "Duck",
"email": "[email protected]",
"oneswipe": False,
},
{
"prename": "Truck",
"postname": "Duck",
"email": "[email protected]",
"oneswipe": False,
},
]
for idx, data in enumerate(demousers):
flask.current_app.db.session.add(
Expand All @@ -151,7 +194,8 @@ def prefill():
coffeebuddy.model.Drink(
userid=random.randint(0, len(demousers)),
price=flask.current_app.config["PRICE"],
timestamp=datetime.datetime.now() - datetime.timedelta(seconds=random.randint(0, 365 * 24 * 60 * 60)),
timestamp=datetime.datetime.now()
- datetime.timedelta(seconds=random.randint(0, 365 * 24 * 60 * 60)),
)
)
flask.current_app.db.session.commit()
Expand Down
8 changes: 6 additions & 2 deletions coffeebuddy/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ def run(self):
last_motion_detected = datetime.datetime.now()
self.events.fire_reset("motion_lost")
self.events.fire("motion_detected")
logging.getLogger(__name__).info(f"Motion detected {last_motion_detected}.")
logging.getLogger(__name__).info(
f"Motion detected {last_motion_detected}."
)
else:
self.events.fire_once("motion_lost")
time.sleep(0.05)
Expand Down Expand Up @@ -95,7 +97,9 @@ def init():
elif flask.current_app.config["CAMERA_ROTATION"] == 180:
flask.current_app.config["CAMERA_ROTATION"] = cv2.ROTATE_180
elif flask.current_app.config["CAMERA_ROTATION"] == 270:
flask.current_app.config["CAMERA_ROTATION"] = cv2.ROTATE_90_COUNTERCLOCKWISE
flask.current_app.config["CAMERA_ROTATION"] = (
cv2.ROTATE_90_COUNTERCLOCKWISE
)
else:
flask.current_app.config["CAMERA_ROTATION"] = None

Expand Down
14 changes: 10 additions & 4 deletions coffeebuddy/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ def run(self):

while True:
try:
request = smartcard.CardRequest.CardRequest(timeout=100, newcardonly=True)
request = smartcard.CardRequest.CardRequest(
timeout=100, newcardonly=True
)
service = request.waitforcard()
service.connection.connect()
uuid = bytes(service.connection.transmit(list(self.PCSC_GET_UUID_APDU))[0])[:4]
uuid = bytes(
service.connection.transmit(list(self.PCSC_GET_UUID_APDU))[0]
)[:4]
if len(uuid) == 4:
self.socketio.emit("card_connected", data={"tag": uuid.hex()})
time.sleep(2)
Expand Down Expand Up @@ -55,8 +59,8 @@ def __init__(self):
self.socketio = flask.current_app.socketio

def run(self):
from RPi import GPIO
import pirc522
from RPi import GPIO

pirc522.RFID.antenna_gain = 0x07
reader = pirc522.RFID(pin_rst=25, pin_irq=24, pin_mode=GPIO.BCM)
Expand All @@ -67,7 +71,9 @@ def run(self):
if not error:
(_, uid) = reader.anticoll()
logging.getLogger(__name__).info(f"Card {uid} connected.")
self.socketio.emit("card_connected", data={"tag": bytes(uid[:4]).hex()})
self.socketio.emit(
"card_connected", data={"tag": bytes(uid[:4]).hex()}
)
break


Expand Down
Loading

0 comments on commit 82ffb9c

Please sign in to comment.