Merge pull request #10 from mdiluz/reformat-cleaup
Big reformat and cleanup
This commit is contained in:
commit
04e9e3a5b1
25 changed files with 61 additions and 93 deletions
|
@ -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:
|
23
.github/workflows/test.yml
vendored
23
.github/workflows/test.yml
vendored
|
@ -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
2
.vscode/launch.json
vendored
|
@ -8,7 +8,7 @@
|
|||
"name": "Python Debugger: Matchy",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "py/matchy.py",
|
||||
"program": "matchy.py",
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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"]
|
|
@ -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
10
py/matchy.py → matchy.py
Executable file → Normal 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
0
matchy/__init__.py
Normal file
0
matchy/cogs/__init__.py
Normal file
0
matchy/cogs/__init__.py
Normal 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}"
|
|
@ -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
0
matchy/files/__init__.py
Normal 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
|
|
@ -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
|
|
@ -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
|
|
@ -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
0
matchy/views/__init__.py
Normal 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")
|
|
@ -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
0
tests/__init__.py
Normal 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
|
|
@ -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()
|
|
@ -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
0
scripts/test-cov.py → tests/test-cov.py
Executable file → Normal 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)
|
||||
|
Loading…
Add table
Reference in a new issue