Add ruff (no automation yet)

This commit is contained in:
Jack Jackson 2024-01-23 22:46:17 -08:00
parent 51d6a85955
commit 9758991ca5
8 changed files with 45 additions and 24 deletions

View File

@ -5,6 +5,8 @@
- [ ] Basic Deck Definition - [ ] Basic Deck Definition
- [X] Figure out how to return JSON or html (`render_template`) - [X] Figure out how to return JSON or html (`render_template`)
- [X] Basic testing - [X] Basic testing
- [ ] ruff
- [ ] GitHub Actions for tests and linters
- [ ] Swagger API - [ ] Swagger API
- [ ] Local development tool to clear/seed database - [ ] Local development tool to clear/seed database
... ...

View File

@ -1 +1,3 @@
# edh-elo [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
This is a score tracker for an EDH ("Commander") group.

View File

@ -6,21 +6,25 @@ from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() db = SQLAlchemy()
def create_app(): def create_app():
app = Flask(__name__) app = Flask(__name__)
secret_key = os.environ.get('SECRET_KEY') secret_key = os.environ.get("SECRET_KEY")
if not secret_key: if not secret_key:
sys.stderr.write('YOU NEED TO SET AN ENV VARIABLE NAMED SECRET_KEY\n') sys.stderr.write("YOU NEED TO SET AN ENV VARIABLE NAMED SECRET_KEY\n")
sys.exit(1) sys.exit(1)
app.config['SECRET_KEY'] = secret_key app.config["SECRET_KEY"] = secret_key
# TODO - support other database types 🙃 # TODO - support other database types 🙃
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URI', 'sqlite:///db.sqlite') app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get(
"DATABASE_URI", "sqlite:///db.sqlite"
)
db.init_app(app) db.init_app(app)
from .main import main as main_blueprint from .main import main as main_blueprint
app.register_blueprint(main_blueprint) app.register_blueprint(main_blueprint)
# TODO - understand how this works, since `db.create_all()` requires that the model classes have already been # TODO - understand how this works, since `db.create_all()` requires that the model classes have already been

View File

@ -4,39 +4,42 @@ from .models import Deck, Game, Player
main = Blueprint("main", __name__) main = Blueprint("main", __name__)
@main.route("/") @main.route("/")
def index(): def index():
return 'Hello, World - but new!' return "Hello, World - but new!"
@main.route("/player", methods=["POST"]) @main.route("/player", methods=["POST"])
def create_player(): def create_player():
data = request.json data = request.json
player = Player( player = Player(name=data["name"])
name=data['name']
)
db.session.add(player) db.session.add(player)
db.session.commit() db.session.commit()
return {'id': player.id} return {"id": player.id}
@main.route("/player/<player_id>") @main.route("/player/<player_id>")
def get_player(player_id: str): def get_player(player_id: str):
player_from_db = db.session.get(Player, int(player_id)) player_from_db = db.session.get(Player, int(player_id))
if not player_from_db: if not player_from_db:
return 'Not Found', 404 return "Not Found", 404
player_data = _jsonify(player_from_db) player_data = _jsonify(player_from_db)
content_type = request.headers.get('Content-Type') content_type = request.headers.get("Content-Type")
if content_type == 'application/json': if content_type == "application/json":
return player_data return player_data
else: # Assume they want HTML else: # Assume they want HTML
return render_template('player_detail.html', **player_data) return render_template("player_detail.html", **player_data)
# TODO - implement a GET method - can it be a separate method, or must it be the same annotation with an `if method==`? # TODO - implement a GET method - can it be a separate method, or must it be the same annotation with an `if method==`?
# Time for testing, methinks! # Time for testing, methinks!
# TODO - would this be better as a method on a class extending `db.Model` that the classes in `models.py` could then # TODO - would this be better as a method on a class extending `db.Model` that the classes in `models.py` could then
# extend? # extend?
# (Probably not, as we'd still need to explicitly call it - it wouldn't be implicitly called _by_ Flask) # (Probably not, as we'd still need to explicitly call it - it wouldn't be implicitly called _by_ Flask)
def _jsonify(o): def _jsonify(o):
return {k: v for (k, v) in o.__dict__.items() if k != '_sa_instance_state'} return {k: v for (k, v) in o.__dict__.items() if k != "_sa_instance_state"}

View File

@ -1,5 +1,6 @@
from . import db from . import db
# Note that a `Player` is "someone who plays in the Pod", whereas `User` (which will be implemented later) is "a user of # Note that a `Player` is "someone who plays in the Pod", whereas `User` (which will be implemented later) is "a user of
# this system". While all Users will _probably_ be Players, they do not have to be - and, it is likely that several # this system". While all Users will _probably_ be Players, they do not have to be - and, it is likely that several
# Players will not be Users (if they don't register to use the system). # Players will not be Users (if they don't register to use the system).
@ -7,12 +8,14 @@ class Player(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False) name = db.Column(db.String, nullable=False)
class Deck(db.Model): class Deck(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(60), nullable=False) name = db.Column(db.String(60), nullable=False)
description = db.Column(db.String) description = db.Column(db.String)
owner = db.Column(db.String, db.ForeignKey(Player.__table__.c.id), nullable=False) owner = db.Column(db.String, db.ForeignKey(Player.__table__.c.id), nullable=False)
class Game(db.Model): class Game(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
# TODO - columns like `location`, `writeups`, etc. # TODO - columns like `location`, `writeups`, etc.

View File

@ -2,3 +2,4 @@ Flask
Flask-SQLAlchemy Flask-SQLAlchemy
flasgger flasgger
flask-login flask-login
ruff

View File

@ -9,16 +9,17 @@ from app import create_app
# https://flask.palletsprojects.com/en/2.3.x/testing/ # https://flask.palletsprojects.com/en/2.3.x/testing/
@pytest.fixture() @pytest.fixture()
def app_fixture(): def app_fixture():
# Start afresh! # Start afresh!
test_database_name = 'testing-db.sqlite' test_database_name = "testing-db.sqlite"
database_location = pathlib.Path('instance').joinpath(test_database_name) database_location = pathlib.Path("instance").joinpath(test_database_name)
if database_location.exists(): if database_location.exists():
database_location.unlink() database_location.unlink()
os.environ['DATABASE_URI'] = f'sqlite:///{test_database_name}' os.environ["DATABASE_URI"] = f"sqlite:///{test_database_name}"
os.environ['SECRET_KEY'] = 'testing-secret-key' os.environ["SECRET_KEY"] = "testing-secret-key"
app = create_app() app = create_app()
# app.config.update({ # app.config.update({
@ -31,10 +32,12 @@ def app_fixture():
# clean up / reset resources here # clean up / reset resources here
@pytest.fixture() @pytest.fixture()
def client(app_fixture): def client(app_fixture):
return app_fixture.test_client() return app_fixture.test_client()
@pytest.fixture() @pytest.fixture()
def runner(app_fixture): def runner(app_fixture):
return app_fixture.test_cli_runner() return app_fixture.test_cli_runner()

View File

@ -1,10 +1,13 @@
# These tests expect that the database starts empty. # These tests expect that the database starts empty.
# TODO: create tests with initialized states # TODO: create tests with initialized states
def test_add_and_retrieve(client): def test_add_and_retrieve(client):
response = client.get('/player/1', headers={'Content-Type': 'application/json'}) response = client.get("/player/1", headers={"Content-Type": "application/json"})
assert response.status_code == 404 assert response.status_code == 404
client.post('/player', headers={'Content-Type': 'application/json'}, json={'name': 'jason'}) client.post(
response_1 = client.get('/player/1', headers={'Content-Type': 'application/json'}) "/player", headers={"Content-Type": "application/json"}, json={"name": "jason"}
assert response_1.json['name'] == 'jason' )
response_1 = client.get("/player/1", headers={"Content-Type": "application/json"})
assert response_1.json["name"] == "jason"