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: | on: | ||||||
|   push: |   push: | ||||||
|  | @ -14,10 +14,32 @@ env: | ||||||
|   REGISTRY: ghcr.io |   REGISTRY: ghcr.io | ||||||
|   IMAGE_NAME: ${{ github.repository }} |   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: | 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: |   build-and-push-images: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|  |     needs: test | ||||||
|      |      | ||||||
|     # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. |     # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. | ||||||
|     permissions: |     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", |             "name": "Python Debugger: Matchy", | ||||||
|             "type": "debugpy", |             "type": "debugpy", | ||||||
|             "request": "launch", |             "request": "launch", | ||||||
|             "program": "py/matchy.py", |             "program": "matchy.py", | ||||||
|             "console": "integratedTerminal" |             "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 |     pip install --find-links /wheels --no-index -r requirements.txt | ||||||
| 
 | 
 | ||||||
| COPY . . | 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. | VSCode can be configured to use this new `.venv` and is the recommended way to develop. | ||||||
| 
 | 
 | ||||||
| ### Tests | ### 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 | ## Hosting | ||||||
| 
 | 
 | ||||||
|  | @ -63,9 +63,9 @@ Matchy is configured by an optional `$MATCHY_CONFIG` envar or a `.matchy/config. | ||||||
| ``` | ``` | ||||||
| Only the version is required. | 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 | ### 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). | 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 | import discord | ||||||
| from discord.ext import commands | from discord.ext import commands | ||||||
| import os | import os | ||||||
| from state import load_from_file | from matchy.files.state import load_from_file | ||||||
| from cogs.matchy_cog import MatchyCog | import matchy.cogs.matchy | ||||||
| from cogs.owner_cog import OwnerCog | import matchy.cogs.owner | ||||||
| 
 | 
 | ||||||
| _STATE_FILE = ".matchy/state.json" | _STATE_FILE = ".matchy/state.json" | ||||||
| state = load_from_file(_STATE_FILE) | state = load_from_file(_STATE_FILE) | ||||||
|  | @ -24,8 +24,8 @@ bot = commands.Bot(command_prefix='$', | ||||||
| 
 | 
 | ||||||
| @bot.event | @bot.event | ||||||
| async def setup_hook(): | async def setup_hook(): | ||||||
|     await bot.add_cog(MatchyCog(bot, state)) |     await bot.add_cog(matchy.cogs.matchy.Cog(bot, state)) | ||||||
|     await bot.add_cog(OwnerCog(bot, state)) |     await bot.add_cog(matchy.cogs.owner.Cog(bot, state)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @bot.event | @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 discord.ext import commands, tasks | ||||||
| from datetime import datetime, timedelta, time | from datetime import datetime, timedelta, time | ||||||
| 
 | 
 | ||||||
| import cogs.match_button as match_button | import matchy.views.match as match | ||||||
| import matching | import matching | ||||||
| from state import State, AuthScope | from matchy.files.state import State, AuthScope | ||||||
| import util | import util | ||||||
| 
 | 
 | ||||||
| logger = logging.getLogger("cog") | logger = logging.getLogger("cog") | ||||||
| logger.setLevel(logging.INFO) | logger.setLevel(logging.INFO) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class MatchyCog(commands.Cog): | class Cog(commands.Cog): | ||||||
|     def __init__(self, bot: commands.Bot, state: State): |     def __init__(self, bot: commands.Bot, state: State): | ||||||
|         self.bot = bot |         self.bot = bot | ||||||
|         self.state = state |         self.state = state | ||||||
|  | @ -25,7 +25,7 @@ class MatchyCog(commands.Cog): | ||||||
|     async def on_ready(self): |     async def on_ready(self): | ||||||
|         """Bot is ready and connected""" |         """Bot is ready and connected""" | ||||||
|         self.run_hourly_tasks.start() |         self.run_hourly_tasks.start() | ||||||
|         self.bot.add_dynamic_items(match_button.DynamicGroupButton) |         self.bot.add_dynamic_items(match.DynamicGroupButton) | ||||||
|         activity = discord.Game("/join") |         activity = discord.Game("/join") | ||||||
|         await self.bot.change_presence(status=discord.Status.online, activity=activity) |         await self.bot.change_presence(status=discord.Status.online, activity=activity) | ||||||
|         logger.info("Bot is up and ready!") |         logger.info("Bot is up and ready!") | ||||||
|  | @ -180,7 +180,7 @@ class MatchyCog(commands.Cog): | ||||||
|             # Otherwise set up the button |             # Otherwise set up the button | ||||||
|             msg += "\n\nClick the button to match up groups and send them to the channel.\n" |             msg += "\n\nClick the button to match up groups and send them to the channel.\n" | ||||||
|             view = discord.ui.View(timeout=None) |             view = discord.ui.View(timeout=None) | ||||||
|             view.add_item(match_button.DynamicGroupButton(members_min)) |             view.add_item(match.DynamicGroupButton(members_min)) | ||||||
|         else: |         else: | ||||||
|             # Let a non-matcher know why they don't have the button |             # Let a non-matcher know why they don't have the button | ||||||
|             msg += f"\n\nYou'll need the {AuthScope.MATCHER}" |             msg += f"\n\nYou'll need the {AuthScope.MATCHER}" | ||||||
|  | @ -3,13 +3,13 @@ Owner bot cog | ||||||
| """ | """ | ||||||
| import logging | import logging | ||||||
| from discord.ext import commands | from discord.ext import commands | ||||||
| from state import State, AuthScope | from matchy.files.state import State, AuthScope | ||||||
| 
 | 
 | ||||||
| logger = logging.getLogger("owner") | logger = logging.getLogger("owner") | ||||||
| logger.setLevel(logging.INFO) | logger.setLevel(logging.INFO) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class OwnerCog(commands.Cog): | class Cog(commands.Cog): | ||||||
|     def __init__(self, bot: commands.Bot, state: State): |     def __init__(self, bot: commands.Bot, state: State): | ||||||
|         self._bot = bot |         self._bot = bot | ||||||
|         self._state = state |         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""" | """Very simple config loading library""" | ||||||
| from schema import Schema, Use, Optional | from schema import Schema, Use, Optional | ||||||
| import files | import matchy.files.ops as ops | ||||||
| import os | import os | ||||||
| import logging | import logging | ||||||
| import json | import json | ||||||
|  | @ -137,7 +137,7 @@ def _load() -> _Config: | ||||||
|     else: |     else: | ||||||
|         # Otherwise try the file |         # Otherwise try the file | ||||||
|         if os.path.isfile(_FILE): |         if os.path.isfile(_FILE): | ||||||
|             loaded = files.load(_FILE) |             loaded = ops.load(_FILE) | ||||||
|             logger.info("Config loaded from %s", _FILE) |             logger.info("Config loaded from %s", _FILE) | ||||||
|         else: |         else: | ||||||
|             loaded = _EMPTY_DICT |             loaded = _EMPTY_DICT | ||||||
|  | @ -4,7 +4,7 @@ from datetime import datetime | ||||||
| from schema import Schema, Use, Optional | from schema import Schema, Use, Optional | ||||||
| from collections.abc import Generator | from collections.abc import Generator | ||||||
| from typing import Protocol | from typing import Protocol | ||||||
| import files | import matchy.files.ops as ops | ||||||
| import copy | import copy | ||||||
| import logging | import logging | ||||||
| from contextlib import contextmanager | from contextlib import contextmanager | ||||||
|  | @ -351,6 +351,7 @@ class State(): | ||||||
|             # Set the value |             # Set the value | ||||||
|             channel[key] = value |             channel[key] = value | ||||||
| 
 | 
 | ||||||
|  |     # TODO: Make this a decorator? | ||||||
|     @contextmanager |     @contextmanager | ||||||
|     def _safe_wrap_write(self): |     def _safe_wrap_write(self): | ||||||
|         """Safely run any function wrapped in a validate""" |         """Safely run any function wrapped in a validate""" | ||||||
|  | @ -369,7 +370,7 @@ class State(): | ||||||
| 
 | 
 | ||||||
|     def _save_to_file(self): |     def _save_to_file(self): | ||||||
|         """Saves the state out to the chosen file""" |         """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): | 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 there's a file load it and try to migrate | ||||||
|     if os.path.isfile(file): |     if os.path.isfile(file): | ||||||
|         loaded = files.load(file) |         loaded = ops.load(file) | ||||||
|         _migrate(loaded) |         _migrate(loaded) | ||||||
| 
 | 
 | ||||||
|     st = State(loaded, file) |     st = State(loaded, file) | ||||||
| 
 | 
 | ||||||
|     # Save out the migrated (or new) file |     # Save out the migrated (or new) file | ||||||
|     files.save(file, st._dict) |     ops.save(file, st._dict) | ||||||
| 
 | 
 | ||||||
|     return st |     return st | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| """Utility functions for matchy""" | """Utility functions for matchy""" | ||||||
| import logging | import logging | ||||||
| import discord | import discord | ||||||
| from datetime import datetime, timedelta | from datetime import datetime | ||||||
| from typing import Protocol, runtime_checkable | from typing import Protocol, runtime_checkable | ||||||
| from state import State, ts_to_datetime | from matchy.files.state import State, ts_to_datetime | ||||||
| import util | import matchy.util as util | ||||||
| import config | import matchy.files.config as config | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class _ScoreFactors(int): | class _ScoreFactors(int): | ||||||
|  | @ -149,14 +149,6 @@ def attempt_create_groups(matchees: list[Member], | ||||||
|     return groups |     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): | def iterate_all_shifts(list: list): | ||||||
|     """Yields each shifted variation of the input list""" |     """Yields each shifted variation of the input list""" | ||||||
|     yield 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') |         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: | def format_day(time: datetime) -> str: | ||||||
|     """Format the a given datetime""" |     """Format the a given datetime""" | ||||||
|     num = get_day_with_suffix(time.day) |     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 discord | ||||||
| import re | import re | ||||||
| 
 | 
 | ||||||
| import state | import matchy.files.state as state | ||||||
| import matching | import matching | ||||||
| 
 | 
 | ||||||
| logger = logging.getLogger("match_button") | 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 | coverage==7.6.1 | ||||||
| discord.py==2.4.0 | discord.py==2.4.0 | ||||||
| dpytest==0.7.0 | dpytest==0.7.0 | ||||||
| flake8==7.1.1 | 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==8.3.2 | ||||||
| pytest-asyncio==0.23.8 | pytest-asyncio==0.23.8 | ||||||
| pytest-cov==5.0.0 | pytest-cov==5.0.0 | ||||||
| schema==0.7.7 | schema==0.7.7 | ||||||
| smmap==5.0.1 |  | ||||||
| yarl==1.9.4 |  | ||||||
|  |  | ||||||
							
								
								
									
										0
									
								
								tests/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/__init__.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -4,8 +4,8 @@ | ||||||
| import discord | import discord | ||||||
| import pytest | import pytest | ||||||
| import random | import random | ||||||
| import matching | import matchy.matching as matching | ||||||
| import state | import matchy.files.state as state | ||||||
| import copy | import copy | ||||||
| import itertools | import itertools | ||||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||||
|  | @ -2,10 +2,10 @@ import discord | ||||||
| import discord.ext.commands as commands | import discord.ext.commands as commands | ||||||
| import pytest | import pytest | ||||||
| import pytest_asyncio | import pytest_asyncio | ||||||
| import state | import matchy.files.state as state | ||||||
| import discord.ext.test as dpytest | 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 | # 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 | # TODO: Test more somehow, though it seems like dpytest is pretty incomplete | ||||||
|  | @ -20,7 +20,7 @@ async def bot(): | ||||||
|     b = commands.Bot(command_prefix="$", |     b = commands.Bot(command_prefix="$", | ||||||
|                      intents=intents) |                      intents=intents) | ||||||
|     await b._async_setup_hook() |     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) |     dpytest.configure(b) | ||||||
|     yield b |     yield b | ||||||
|     await dpytest.empty_queue() |     await dpytest.empty_queue() | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| """ | """ | ||||||
|     Test functions for the state module |     Test functions for the state module | ||||||
| """ | """ | ||||||
| import state | import matchy.files.state as state | ||||||
| import tempfile | import tempfile | ||||||
| import os | 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 | # Run flake | ||||||
| app = Application() | 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() | flake_exitcode = app.exit_code() | ||||||
| print(flake_exitcode) | print(flake_exitcode) | ||||||
| 
 | 
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue