Merge pull request #10 from mdiluz/reformat-cleaup

Big reformat and cleanup
This commit is contained in:
Marc Di Luzio 2024-08-14 22:56:48 +01:00 committed by GitHub
commit 04e9e3a5b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 61 additions and 93 deletions

View file

@ -1,4 +1,4 @@
name: Build and Push Docker
name: Run Tests
on:
push:
@ -14,10 +14,32 @@ env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
jobs:
# Core test runner
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.11
cache: pip
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
- name: Run tests
run: |
python tests/test.py
build-and-push-images:
runs-on: ubuntu-latest
needs: test
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
permissions:

View file

@ -1,23 +0,0 @@
name: Run Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.11
cache: pip
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
- name: Run tests
run: |
python scripts/test.py

2
.vscode/launch.json vendored
View file

@ -8,7 +8,7 @@
"name": "Python Debugger: Matchy",
"type": "debugpy",
"request": "launch",
"program": "py/matchy.py",
"program": "matchy.py",
"console": "integratedTerminal"
}
]

View file

@ -17,4 +17,4 @@ RUN --mount=type=cache,target=/var/cache/buildkit/pip \
pip install --find-links /wheels --no-index -r requirements.txt
COPY . .
CMD ["python", "py/matchy.py"]
CMD ["python", "matchy.py"]

View file

@ -42,7 +42,7 @@ git checkout -b [feature-branch-name]
VSCode can be configured to use this new `.venv` and is the recommended way to develop.
### Tests
Python tests are written to use `pytest` and cover most internal functionality. Tests can be run in the same way as in the Github Actions with [`scripts/test.py`](`scripts/test.py`), which lints all python code and runs any tests with `pytest`. A helper script [`scripts/test-cov.py`](scripts/test-cov.py) is available to generate a html view on current code coverage.
Python tests are written to use `pytest` and cover most internal functionality. Tests can be run in the same way as in the Github Actions with [`test.py`](`tests/test.py`), which lints all python code and runs any tests with `pytest`. A helper script [`test-cov.py`](tests/test-cov.py) is available to generate a html view on current code coverage.
## Hosting
@ -63,9 +63,9 @@ Matchy is configured by an optional `$MATCHY_CONFIG` envar or a `.matchy/config.
```
Only the version is required.
See [`py/config.py`](py/config.py) for explanations for any extra settings here.
See [`config.py`](matchy/files/config.py) for explanations for any extra settings here.
_State_ is stored locally in a `.matchy/state.json` file. This will be created by the bot. This stores historical information on users, maching schedules, user auth scopes and more. See [`py/state.py`](py/state.py) for schema information if you need to inspect it.
_State_ is stored locally in a `.matchy/state.json` file. This will be created by the bot. This stores historical information on users, maching schedules, user auth scopes and more. See [`state.py`](matchy/files/state.py) for schema information if you need to inspect it.
### Secrets
The `TOKEN` envar is required run the bot. It's recommended this is placed in a local `.env` file. To generate bot token for development see [this discord.py guide](https://discordpy.readthedocs.io/en/stable/discord.html).

10
py/matchy.py → matchy.py Executable file → Normal file
View file

@ -5,9 +5,9 @@ import logging
import discord
from discord.ext import commands
import os
from state import load_from_file
from cogs.matchy_cog import MatchyCog
from cogs.owner_cog import OwnerCog
from matchy.files.state import load_from_file
import matchy.cogs.matchy
import matchy.cogs.owner
_STATE_FILE = ".matchy/state.json"
state = load_from_file(_STATE_FILE)
@ -24,8 +24,8 @@ bot = commands.Bot(command_prefix='$',
@bot.event
async def setup_hook():
await bot.add_cog(MatchyCog(bot, state))
await bot.add_cog(OwnerCog(bot, state))
await bot.add_cog(matchy.cogs.matchy.Cog(bot, state))
await bot.add_cog(matchy.cogs.owner.Cog(bot, state))
@bot.event

0
matchy/__init__.py Normal file
View file

0
matchy/cogs/__init__.py Normal file
View file

View file

@ -7,16 +7,16 @@ from discord import app_commands
from discord.ext import commands, tasks
from datetime import datetime, timedelta, time
import cogs.match_button as match_button
import matchy.views.match as match
import matching
from state import State, AuthScope
from matchy.files.state import State, AuthScope
import util
logger = logging.getLogger("cog")
logger.setLevel(logging.INFO)
class MatchyCog(commands.Cog):
class Cog(commands.Cog):
def __init__(self, bot: commands.Bot, state: State):
self.bot = bot
self.state = state
@ -25,7 +25,7 @@ class MatchyCog(commands.Cog):
async def on_ready(self):
"""Bot is ready and connected"""
self.run_hourly_tasks.start()
self.bot.add_dynamic_items(match_button.DynamicGroupButton)
self.bot.add_dynamic_items(match.DynamicGroupButton)
activity = discord.Game("/join")
await self.bot.change_presence(status=discord.Status.online, activity=activity)
logger.info("Bot is up and ready!")
@ -180,7 +180,7 @@ class MatchyCog(commands.Cog):
# Otherwise set up the button
msg += "\n\nClick the button to match up groups and send them to the channel.\n"
view = discord.ui.View(timeout=None)
view.add_item(match_button.DynamicGroupButton(members_min))
view.add_item(match.DynamicGroupButton(members_min))
else:
# Let a non-matcher know why they don't have the button
msg += f"\n\nYou'll need the {AuthScope.MATCHER}"

View file

@ -3,13 +3,13 @@ Owner bot cog
"""
import logging
from discord.ext import commands
from state import State, AuthScope
from matchy.files.state import State, AuthScope
logger = logging.getLogger("owner")
logger.setLevel(logging.INFO)
class OwnerCog(commands.Cog):
class Cog(commands.Cog):
def __init__(self, bot: commands.Bot, state: State):
self._bot = bot
self._state = state

0
matchy/files/__init__.py Normal file
View file

View file

@ -1,6 +1,6 @@
"""Very simple config loading library"""
from schema import Schema, Use, Optional
import files
import matchy.files.ops as ops
import os
import logging
import json
@ -137,7 +137,7 @@ def _load() -> _Config:
else:
# Otherwise try the file
if os.path.isfile(_FILE):
loaded = files.load(_FILE)
loaded = ops.load(_FILE)
logger.info("Config loaded from %s", _FILE)
else:
loaded = _EMPTY_DICT

View file

@ -4,7 +4,7 @@ from datetime import datetime
from schema import Schema, Use, Optional
from collections.abc import Generator
from typing import Protocol
import files
import matchy.files.ops as ops
import copy
import logging
from contextlib import contextmanager
@ -351,6 +351,7 @@ class State():
# Set the value
channel[key] = value
# TODO: Make this a decorator?
@contextmanager
def _safe_wrap_write(self):
"""Safely run any function wrapped in a validate"""
@ -369,7 +370,7 @@ class State():
def _save_to_file(self):
"""Saves the state out to the chosen file"""
files.save(self._file, self.dict_internal_copy)
ops.save(self._file, self.dict_internal_copy)
def _migrate(dict: dict):
@ -390,12 +391,12 @@ def load_from_file(file: str) -> State:
# If there's a file load it and try to migrate
if os.path.isfile(file):
loaded = files.load(file)
loaded = ops.load(file)
_migrate(loaded)
st = State(loaded, file)
# Save out the migrated (or new) file
files.save(file, st._dict)
ops.save(file, st._dict)
return st

View file

@ -1,11 +1,11 @@
"""Utility functions for matchy"""
import logging
import discord
from datetime import datetime, timedelta
from datetime import datetime
from typing import Protocol, runtime_checkable
from state import State, ts_to_datetime
import util
import config
from matchy.files.state import State, ts_to_datetime
import matchy.util as util
import matchy.files.config as config
class _ScoreFactors(int):
@ -149,14 +149,6 @@ def attempt_create_groups(matchees: list[Member],
return groups
def datetime_range(start_time: datetime, increment: timedelta, end: datetime):
"""Yields a datetime range with a given increment"""
current = start_time
while current <= end or end is None:
yield current
current += increment
def iterate_all_shifts(list: list):
"""Yields each shifted variation of the input list"""
yield list

View file

@ -9,11 +9,6 @@ def get_day_with_suffix(day):
return str(day) + {1: 'st', 2: 'nd', 3: 'rd'}.get(day % 10, 'th')
def format_today() -> str:
"""Format the current datetime"""
return format_day(datetime.now())
def format_day(time: datetime) -> str:
"""Format the a given datetime"""
num = get_day_with_suffix(time.day)

0
matchy/views/__init__.py Normal file
View file

View file

@ -5,7 +5,7 @@ import logging
import discord
import re
import state
import matchy.files.state as state
import matching
logger = logging.getLogger("match_button")

View file

@ -1,27 +1,8 @@
aiohappyeyeballs==2.3.5
aiohttp==3.10.3
aiosignal==1.3.1
attrs==24.2.0
autopep8==2.3.1
coverage==7.6.1
discord.py==2.4.0
dpytest==0.7.0
flake8==7.1.1
frozenlist==1.4.1
gitdb==4.0.11
GitPython==3.1.43
idna==3.7
iniconfig==2.0.0
mccabe==0.7.0
multidict==6.0.5
overrides==7.7.0
packaging==24.1
pluggy==1.5.0
pycodestyle==2.12.1
pyflakes==3.2.0
pytest==8.3.2
pytest-asyncio==0.23.8
pytest-cov==5.0.0
schema==0.7.7
smmap==5.0.1
yarl==1.9.4
schema==0.7.7

0
tests/__init__.py Normal file
View file

View file

@ -4,8 +4,8 @@
import discord
import pytest
import random
import matching
import state
import matchy.matching as matching
import matchy.files.state as state
import copy
import itertools
from datetime import datetime, timedelta

View file

@ -2,10 +2,10 @@ import discord
import discord.ext.commands as commands
import pytest
import pytest_asyncio
import state
import matchy.files.state as state
import discord.ext.test as dpytest
from cogs.owner_cog import OwnerCog
from matchy.cogs.owner import Cog
# Primarily borrowing from https://dpytest.readthedocs.io/en/latest/tutorials/using_pytest.html
# TODO: Test more somehow, though it seems like dpytest is pretty incomplete
@ -20,7 +20,7 @@ async def bot():
b = commands.Bot(command_prefix="$",
intents=intents)
await b._async_setup_hook()
await b.add_cog(OwnerCog(b, state.State(state._EMPTY_DICT)))
await b.add_cog(Cog(b, state.State(state._EMPTY_DICT)))
dpytest.configure(b)
yield b
await dpytest.empty_queue()

View file

@ -1,7 +1,7 @@
"""
Test functions for the state module
"""
import state
import matchy.files.state as state
import tempfile
import os

0
scripts/test-cov.py → tests/test-cov.py Executable file → Normal file
View file

View file

@ -4,7 +4,7 @@ from flake8.main.application import Application
# Run flake
app = Application()
ret = app.run(["--max-line-length", "120", "py/", "scripts/"])
ret = app.run(["--max-line-length", "120", "matchy/", "tests/"])
flake_exitcode = app.exit_code()
print(flake_exitcode)