diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..07f72d6 --- /dev/null +++ b/.flake8 @@ -0,0 +1,11 @@ +[flake8] +max-line-length=120 +ignore = + E121, + E123, + E126, + E226, + E24, + E704, + W605 +exclude = ./tests diff --git a/.gitignore b/.gitignore index 7655200..b8acba0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ build/ dist/ __pycache__ *.egg-info +idea diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..58734b9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,37 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-yaml + - id: check-added-large-files + args: ['--maxkb=10240'] + - id: check-merge-conflict + - id: end-of-file-fixer + - id: trailing-whitespace + +# isort -- sorts imports +- repo: https://github.com/timothycrosley/isort + rev: 5.13.2 + hooks: + - id: isort + args: ["--profile", "black", "--filter-files"] + +# Flake8 +#- repo: https://github.com/pycqa/flake8 +# rev: '7.0.0' +# hooks: +# - id: flake8 + +# Black +# Using this mirror lets us use mypyc-compiled black, which is about 2x faster +- repo: https://github.com/psf/black-pre-commit-mirror + rev: 24.2.0 + hooks: + - id: black + language_version: python3.8 + +# Poetry +- repo: https://github.com/python-poetry/poetry + rev: 1.8.2 + hooks: + - id: poetry-lock diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..143c2f5 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.8.19 diff --git a/LICENSE-MIT b/LICENSE-MIT index 5b62efb..653c70f 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,6 +1,6 @@ SmileyFace Unreal Tournament 4 Hub Automator -Copyright (c) 2020 Mathew Guest +Copyright (c) 2024 Mathew Guest Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..f6af3ef --- /dev/null +++ b/poetry.lock @@ -0,0 +1,440 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "app-skellington" +version = "0.1.1" +description = "A high-powered command line menu framework." +optional = false +python-versions = ">=3" +files = [ + {file = "app_skellington-0.1.1-py3-none-any.whl", hash = "sha256:61853a63b6683a3bccbca87c34d19352c1c5c4ab80fb4ff5fa77ebc0cf3348af"}, + {file = "app_skellington-0.1.1.tar.gz", hash = "sha256:d9ea7423f8e9434724065e528f89b35182abe9b338e6997397d46e98e7f87a0a"}, +] + +[package.dependencies] +appdirs = "*" +colorlog = "*" +configobj = "*" + +[[package]] +name = "appdirs" +version = "1.4.4" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = "*" +files = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] + +[[package]] +name = "black" +version = "24.4.2" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, + {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, + {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, + {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, + {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, + {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, + {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, + {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, + {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, + {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, + {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, + {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, + {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, + {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, + {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, + {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, + {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, + {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, + {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, + {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, + {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, + {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "colorlog" +version = "6.8.2" +description = "Add colours to the output of Python's logging module." +optional = false +python-versions = ">=3.6" +files = [ + {file = "colorlog-6.8.2-py3-none-any.whl", hash = "sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33"}, + {file = "colorlog-6.8.2.tar.gz", hash = "sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +development = ["black", "flake8", "mypy", "pytest", "types-colorama"] + +[[package]] +name = "configobj" +version = "5.0.8" +description = "Config file reading, writing and validation." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "configobj-5.0.8-py2.py3-none-any.whl", hash = "sha256:a7a8c6ab7daade85c3f329931a807c8aee750a2494363934f8ea84d8a54c87ea"}, + {file = "configobj-5.0.8.tar.gz", hash = "sha256:6f704434a07dc4f4dc7c9a745172c1cad449feb548febd9f7fe362629c627a97"}, +] + +[package.dependencies] +six = "*" + +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + +[[package]] +name = "filelock" +version = "3.15.4" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, + {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "flake8" +version = "5.0.4" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, + {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.9.0,<2.10.0" +pyflakes = ">=2.5.0,<2.6.0" + +[[package]] +name = "identify" +version = "2.6.0" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, + {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pre-commit" +version = "3.5.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, + {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "pycodestyle" +version = "2.9.1" +description = "Python style guide checker" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, + {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, +] + +[[package]] +name = "pyflakes" +version = "2.5.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, + {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "virtualenv" +version = "20.26.3" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, + {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "c2001f64c789fcc98f1fc1702a4a511e788776a0c7d4c438d66193f0a1241c0c" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..db85cd6 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,51 @@ +[tool.poetry] +name = "smileyface" +version = "0.1.0" +description = "smileyface UT4 hub automator hosting" +authors = [ + "Mathew Guest ", +] +license = "MIT" +readme = "README.md" +homepage = "https://zavage-software.com/portfolio/smileyface" +repository = "https://git-mirror.zavage.net/zavage-software/smileyface" +documentation = "https://git-mirror.zavage.net/zavage-software/smileyface" +keywords = ["cas"] + +packages = [{ include = "smileyface" }] +include = [ + "README.md", +] + +# [tool.poetry.scripts] + + +[tool.poetry.dependencies] +python = "^3.8" +app_skellington = "*" +configobj = "*" +colorlog = "*" +appdirs = "*" + +[tool.poetry.group.dev.dependencies] +black = "*" +pre-commit = "*" +isort = "*" +flake8 = "*" +#Sphinx = "^5.3.0" +#sphinx-rtd-theme = "^1.3.0" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.black] +line-length = 120 +target-version = ['py38'] + +[tool.isort] +multi_line_output = 3 +combine_as_imports = true +include_trailing_comma = true +force_grid_wrap = 3 +ensure_newline_before_comments = true diff --git a/setup.py b/setup.py index 2704511..8cc6204 100755 --- a/setup.py +++ b/setup.py @@ -1,35 +1,25 @@ #!/usr/bin/env python -from setuptools import setup, find_packages +from setuptools import find_packages, setup -__project__ = 'SmileyFace UT4 Hub Automator' -__version__ = '0.1.0' +__project__ = "SmileyFace UT4 Hub Automator" +__version__ = "0.1.0" app_skellington_requirements = ( - 'appdirs', - 'colorlog', - 'configobj', + "appdirs", + "colorlog", + "configobj", ) setup( - name = __project__, - version = __version__, - description = 'Unreal Tournament 4 Server Admin and Control Panel', - author = 'Mathew Guest', - author_email = 't3h.zavage@gmail.com', - url = 'https://git-mirror.zavage-software.com', - + name=__project__, + version=__version__, + description="Unreal Tournament 4 Server Admin and Control Panel", + author="Mathew Guest", + author_email="t3h.zavage@gmail.com", + url="https://git-mirror.zavage-software.com", # Third-party dependencies; will be automatically installed - install_requires = ( - 'rdiff-backup', - 'app_skellington', - 'appdirs', - 'sqlparse' - ) + app_skellington_requirements, - - packages = find_packages(), - package_dir = { - 'app_skellington': 'lib/app_skellington' - }, + install_requires=("rdiff-backup", "app_skellington", "appdirs", "sqlparse") + app_skellington_requirements, + packages=find_packages(), + package_dir={"app_skellington": "lib/app_skellington"}, ) - diff --git a/smileyface.py b/smileyface.py index 2aa1b6d..09dfec2 100755 --- a/smileyface.py +++ b/smileyface.py @@ -1,4 +1,4 @@ #!/usr/bin/env python import smileyface -smileyface.start_app() +smileyface.start_app() diff --git a/smileyface/__init__.py b/smileyface/__init__.py index 471b833..3f0dd3a 100644 --- a/smileyface/__init__.py +++ b/smileyface/__init__.py @@ -2,18 +2,20 @@ import logging import sys # Module parameters and constants -APP_NAME = 'SmileyFace Unreal Tournament 4 Server Panel' -APP_AUTHOR = 'Mathew Guest' -APP_VERSION = '0.1.0' +APP_NAME = "SmileyFace Unreal Tournament 4 Server Panel" +APP_AUTHOR = "Mathew Guest" +APP_VERSION = "0.1.0" -APP_CONFIG_FILENAME = 'config.ini' +APP_CONFIG_FILENAME = "config.ini" # config.spec is relative to the module src directory and is the # config specification (structure, names, and types of config file) -APP_CONFIGSPEC_FILENAME = 'config.spec' +APP_CONFIGSPEC_FILENAME = "config.spec" # Check and gracefully fail if the user needs to install a 3rd-party dep. -required_lib_names = ['appdirs', 'configobj', 'colorlog'] +required_lib_names = ["appdirs", "configobj", "colorlog"] + + def check_env_has_dependencies(required_lib_names): """ Attempts to import each module and gracefully fails if it doesn't @@ -24,15 +26,17 @@ def check_env_has_dependencies(required_lib_names): try: __import__(libname) except ImportError as ex: - print('missing third-part library: ', ex, file=sys.stderr) + print("missing third-part library: ", ex, file=sys.stderr) rc = False except Exception as ex: print(ex, type(ex)) rc = False return rc + + if not check_env_has_dependencies(required_lib_names): - print('refusing to load program without installed dependencies', file=sys.stderr) - raise ImportError('python environment needs third-party dependencies installed') + print("refusing to load program without installed dependencies", file=sys.stderr) + raise ImportError("python environment needs third-party dependencies installed") # Exposed from sub-modules: from .app import start_app diff --git a/smileyface/_util.py b/smileyface/_util.py index 9c9aaa3..16f42c4 100644 --- a/smileyface/_util.py +++ b/smileyface/_util.py @@ -1,11 +1,11 @@ -import hashlib import functools +import hashlib + def md5sum_file(filename): - with open(filename, mode='rb') as f: + with open(filename, mode="rb") as f: d = hashlib.md5() - for buf in iter(functools.partial(f.read, 128), b''): + for buf in iter(functools.partial(f.read, 128), b""): d.update(buf) h = d.hexdigest() return h - diff --git a/smileyface/app.py b/smileyface/app.py index 62154b3..f7cbb34 100644 --- a/smileyface/app.py +++ b/smileyface/app.py @@ -1,63 +1,49 @@ -from . import hub_machine -from . import datalayer -from . import scrape_latest - import app_skellington from app_skellington import _util +from . import ( + datalayer, + hub_machine, + scrape_latest, +) + class SmileyFace(app_skellington.ApplicationContainer): def __init__(self, *args, **kwargs): - filename = 'config.spec' + filename = "config.spec" self.configspec_filepath = _util.get_asset(__name__, filename) - config_filepath = self._get_config_filepath( - 'smileyface-ut4', - '', - 'hub-config.ini' - ) + config_filepath = self._get_config_filepath("smileyface-ut4", "", "hub-config.ini") super().__init__( configspec_filepath=self.configspec_filepath, configini_filepath=config_filepath, - app_name = 'SmileyFace UT4 Server Panel', - app_author = 'Mathew Guest', - app_version = '0.1', + app_name="SmileyFace UT4 Server Panel", + app_author="Mathew Guest", + app_version="0.1", *args, - **kwargs + **kwargs, ) def _cli_options(self): pass def _command_menu(self): - sm_root = self.cli.init_submenu('command') - _util.register_class_as_commands( - self, sm_root, - hub_machine.UT4ServerMachine - ) + sm_root = self.cli.init_submenu("command") + _util.register_class_as_commands(self, sm_root, hub_machine.UT4ServerMachine) - sm_scrape = sm_root.create_submenu('scrape') - _util.register_class_as_commands( - self, sm_scrape, - scrape_latest.ScrapeUt4Pugs - ) + sm_scrape = sm_root.create_submenu("scrape") + _util.register_class_as_commands(self, sm_scrape, scrape_latest.ScrapeUt4Pugs) - _util.register_class_as_commands( - self, sm_scrape, - scrape_latest.ScrapeUtcc - ) + _util.register_class_as_commands(self, sm_scrape, scrape_latest.ScrapeUtcc) - _util.register_class_as_commands( - self, sm_scrape, - scrape_latest.LocalFs - ) + _util.register_class_as_commands(self, sm_scrape, scrape_latest.LocalFs) def _services(self): - self['model'] = lambda: hub_machine.UTServerMachine(self.ctx) + self["model"] = lambda: hub_machine.UTServerMachine(self.ctx) self.dal = datalayer.DataLayer(self.ctx) - self['dal'] = lambda: self.dal - self['datalayer'] = lambda: datalayer.DbFuncs(self.ctx, self.dal) + self["dal"] = lambda: self.dal + self["datalayer"] = lambda: datalayer.DbFuncs(self.ctx, self.dal) # self['localfs'] = lambda: datalayer.LocalFs(self.ctx, datalayer) @@ -67,7 +53,7 @@ class SmileyFace(app_skellington.ApplicationContainer): def invoke_from_cli(self): rc = self.load_command() if not rc: - print('Invalid command. Try -h for usage') + print("Invalid command. Try -h for usage") return # load config self.invoke_command() @@ -136,10 +122,9 @@ Typical Usage: ./ut4-server-ctl.sh upload-server ./ut4-server-ctl.sh restart-server """ - print(s) + print(s) def start_app(): app = SmileyFace() app.invoke_from_cli() - diff --git a/smileyface/datalayer/datalayer.py b/smileyface/datalayer/datalayer.py index 3690941..8f9b9cb 100644 --- a/smileyface/datalayer/datalayer.py +++ b/smileyface/datalayer/datalayer.py @@ -1,8 +1,9 @@ -from smileyface import myutil +import os +import sqlite3 import appdirs -import sqlite3 -import os + +from smileyface import myutil class DataLayer: @@ -17,10 +18,10 @@ class DataLayer: return self._db_conn def _create_db_connection(self): - local_db_filename = self.ctx.config['app']['sqlite_filename'] - appdir = appdirs.user_data_dir('smileyface') + local_db_filename = self.ctx.config["app"]["sqlite_filename"] + appdir = appdirs.user_data_dir("smileyface") fullpath = os.path.join(appdir, local_db_filename) - self.ctx.log['ut4'].info('sqlite3 filename: %s', fullpath) + self.ctx.log["ut4"].info("sqlite3 filename: %s", fullpath) myutil.ensure_dir_exists(fullpath) @@ -28,9 +29,5 @@ class DataLayer: return db def commit(self): - self.ctx.log['db'].info('commit()') + self.ctx.log["db"].info("commit()") self.db_conn.commit() - - - - diff --git a/smileyface/datalayer/db_ops.py b/smileyface/datalayer/db_ops.py index febaa86..bb5f090 100644 --- a/smileyface/datalayer/db_ops.py +++ b/smileyface/datalayer/db_ops.py @@ -1,12 +1,12 @@ -from smileyface import myutil -from smileyface import structs +import datetime +import os import app_skellington._util as apputil import appdirs -import datetime -import os import sqlparse +from smileyface import myutil, structs + class DbFuncs: def __init__(self, ctx, dal): @@ -14,7 +14,7 @@ class DbFuncs: self.dal = dal def create_tables(self): - sql_filename = apputil.get_asset(__name__, 'create_schema.sql') + sql_filename = apputil.get_asset(__name__, "create_schema.sql") with open(sql_filename) as fp: contents_sql = fp.read() stmts = sqlparse.split(contents_sql) @@ -22,7 +22,7 @@ class DbFuncs: conn = self.dal.db_conn curs = conn.cursor() for stmt in stmts: - print('----') + print("----") print(stmt) curs.execute(stmt) @@ -31,9 +31,9 @@ class DbFuncs: def truncate_tables(self): conn = self.dal.db_conn curs = conn.cursor() - sql = ''' + sql = """ truncate file_paks; - ''' + """ for stmt in sqlparse.split(sql): curs.execute(stmt) self.dal.commit() @@ -43,7 +43,7 @@ truncate file_paks; # NOTE(MG) datetime parameter logic could be improved and more complete conn = self.dal.db_conn curs = conn.cursor() - sql = ''' + sql = """ insert into file_paks ( file_pak_id, fullpath, @@ -63,12 +63,12 @@ on conflict(filename) do update set md5sum = excluded.md5sum, --created at does not update record_updated_at = datetime('now', 'localtime') - ''' + """ args = ( record.file_pak_id, record.fullpath, record.filename, - record.md5sum + record.md5sum, # record.record_created_at, # record.record_updated_at ) @@ -80,7 +80,7 @@ on conflict(filename) do update set # validation data src conn = self.dal.db_conn curs = conn.cursor() - sql = ''' + sql = """ update file_paks set validated_state = ?, @@ -90,7 +90,7 @@ set where file_pak_id = ? -''' +""" args = (validate_state, src, remote_src_md5, rec_id) curs.execute(sql, args) conn.commit() @@ -98,14 +98,14 @@ where def query_filepak(self, filename): conn = self.dal.db_conn curs = conn.cursor() - sql = ''' + sql = """ select file_pak_id, fullpath, filename, md5sum, record_created_at, record_updated_at from file_paks where - lower(filename) = lower(?)''' + lower(filename) = lower(?)""" args = (filename,) # print(sql) curs.execute(sql, args) @@ -126,15 +126,14 @@ where elif len(output) == 1: return output[0] elif len(output) > 1: - input(' unexpected two rows returned from db when expecting to be unique') + input(" unexpected two rows returned from db when expecting to be unique") return output return output - def query_invalid_filepaks(self): conn = self.dal.db_conn curs = conn.cursor() - sql = ''' + sql = """ select file_pak_id, fullpath, filename, md5sum, @@ -143,7 +142,7 @@ select from file_paks where - lower(filename) = lower(?)''' + lower(filename) = lower(?)""" args = (filename,) # print(sql) curs.execute(sql, args) @@ -160,7 +159,3 @@ where filepak.record_updated_at = r[8] output.append(filepak) return output - - - - diff --git a/smileyface/gameconfig_edit.py b/smileyface/gameconfig_edit.py index 91f20e0..fccae7b 100644 --- a/smileyface/gameconfig_edit.py +++ b/smileyface/gameconfig_edit.py @@ -1,6 +1,7 @@ import configparser import re + class UnrealIniFile: def __init__(self, filename=None): self._config = None @@ -14,12 +15,11 @@ class UnrealIniFile: def filename(self, val): self._filename = val if val is not None: - self._config = configparser.RawConfigParser( - strict=False - ) - self._config.optionxform = str # Trick to preserve case in key names + self._config = configparser.RawConfigParser(strict=False) + self._config.optionxform = str # Trick to preserve case in key names self._config.read(self._filename) + class GameIniSpecial: def __init__(self, filename): self._redirect_lines = [] @@ -37,24 +37,23 @@ class GameIniSpecial: def clear_redirect_references(self): self._redirect_lines = [] - def add_redirect_reference( - self, pkg_basename, redirect_url, redirect_protocol, - relative_path, md5sum - ): + def add_redirect_reference(self, pkg_basename, redirect_url, redirect_protocol, relative_path, md5sum): args = { - 'pkg_basename': pkg_basename, - 'redirect_protocol': redirect_protocol, - 'redirect_url': redirect_url, - 'relative_path': relative_path, - 'md5sum': md5sum + "pkg_basename": pkg_basename, + "redirect_protocol": redirect_protocol, + "redirect_url": redirect_url, + "relative_path": relative_path, + "md5sum": md5sum, } -########### START multi-line awkward indent + ########### START multi-line awkward indent line = '\ RedirectReferences=(PackageName="{pkg_basename}",\ PackageURLProtocol="{redirect_protocol}",\ PackageURL="{redirect_url}/{relative_path}",\ -PackageChecksum="{md5sum}")'.format(**args) -########### END multi-line awkward indent +PackageChecksum="{md5sum}")'.format( + **args + ) + ########### END multi-line awkward indent return self.add_redirect_reference_line(line) @@ -64,19 +63,14 @@ PackageChecksum="{md5sum}")'.format(**args) def write(self, fp): newcontents = None - - with open(self.filename, 'r') as inifile: + + with open(self.filename, "r") as inifile: curcontents = inifile.read() - lines_str = '\n'.join(self._redirect_lines) - - newcontents = re.sub( - 'RedirectReferences = :PARAM:', - lines_str, - curcontents - ) + lines_str = "\n".join(self._redirect_lines) + + newcontents = re.sub("RedirectReferences = :PARAM:", lines_str, curcontents) has_data = True if has_data: - with open(self.filename, 'w') as fp: + with open(self.filename, "w") as fp: fp.write(newcontents) - diff --git a/smileyface/hub_machine.py b/smileyface/hub_machine.py index f5b2373..92696f3 100644 --- a/smileyface/hub_machine.py +++ b/smileyface/hub_machine.py @@ -1,10 +1,4 @@ -from .gameconfig_edit import UnrealIniFile, GameIniSpecial -from ._util import md5sum_file -from . import myutil -from . import structs - import collections -import configobj import configparser import datetime import glob @@ -14,6 +8,12 @@ import subprocess import sys import time +import configobj + +from . import myutil, structs +from ._util import md5sum_file +from .gameconfig_edit import GameIniSpecial, UnrealIniFile + class UT4ServerMachine: def __init__(self, ctx, datalayer): @@ -23,8 +23,6 @@ class UT4ServerMachine: if not self._validate_env_vars(): sys.exit(1) - - def oneclickdeploy(self): self.generate_instance() self.upload_redirects() @@ -34,7 +32,7 @@ class UT4ServerMachine: """ Deletes the generated instance on the local machine. """ - self.ctx.log['ut4'].info('Clearing .pak folder...') + self.ctx.log["ut4"].info("Clearing .pak folder...") cmd = 'rm -rv "$PROJECT_DIR"/instance/LinuxServer/UnrealTournament/Content/Paks/*' self._invoke_command(cmd) @@ -42,61 +40,46 @@ class UT4ServerMachine: """ Create required directories which the user installs maps, mutators, and config to. """ - dirs = ( - 'base', - 'files/config', - 'files/maps', - 'files/mutators', - 'files/rulesets', - 'files/unused' - ) - project_dir = self.ctx.config['app']['project_dir'] + dirs = ("base", "files/config", "files/maps", "files/mutators", "files/rulesets", "files/unused") + project_dir = self.ctx.config["app"]["project_dir"] if len(project_dir.strip()) == 0: - project_dir = '.' - print('project_dir:', project_dir) - fullpaths = [ - '/'.join([project_dir, d]) for d in dirs - ] + project_dir = "." + print("project_dir:", project_dir) + fullpaths = ["/".join([project_dir, d]) for d in dirs] for fp in fullpaths: - cmd = 'mkdir -p {}'.format(fp) + cmd = "mkdir -p {}".format(fp) self._invoke_command(cmd) def download_linux_server(self, x): """ Download the latest Linux Unreal Tournament 4 Server from Epic """ - self.ctx.log['ut4'].info('Downloading Linux Server Binary from Epic.') + self.ctx.log["ut4"].info("Downloading Linux Server Binary from Epic.") def download_logs(self): """ Download the logs from the target hub. """ - config_dir = self.ctx.config['app']['config_dir'] - remote_game_host = self.ctx.config['app']['remote_game_host'] - remote_game_dir = self.ctx.config['app']['remote_game_dir'] + config_dir = self.ctx.config["app"]["config_dir"] + remote_game_host = self.ctx.config["app"]["remote_game_host"] + remote_game_dir = self.ctx.config["app"]["remote_game_dir"] - self.ctx.log['ut4'].info('Downloading instance logs from target hub.') - cmd = ''' + self.ctx.log["ut4"].info("Downloading instance logs from target hub.") + cmd = """ rsync -ravzp {remote_game_host}:{remote_game_dir}/LinuxServer/UnrealTournament/Saved/Logs/ {config_dir}/downloaded-logs/ -'''\ -.format(**{ - 'config_dir': config_dir, - 'remote_game_host': remote_game_host, - 'remote_game_dir': remote_game_dir -}) +""".format( + **{"config_dir": config_dir, "remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir} + ) self._invoke_command(cmd) # Delete logs on remote game server if successfully transferred to local: - self.ctx.log['ut4'].info('') - cmd = ''' + self.ctx.log["ut4"].info("") + cmd = """ ssh {remote_game_host} rm {remote_game_dir}/LinuxServer/UnrealTournament/Saved/Logs/* -r' -'''\ -.format(**{ - 'config_dir': config_dir, - 'remote_game_host': remote_game_host, - 'remote_game_dir': remote_game_dir -}) +""".format( + **{"config_dir": config_dir, "remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir} + ) # self._invoke_command(cmd) def generate_instance(self): @@ -104,34 +87,25 @@ ssh {remote_game_host} rm {remote_game_dir}/LinuxServer/UnrealTournament/Saved/L Takes the current coniguration and outputs the application files which can be copied to the server. """ - self.ctx.log['ut4'].info('Generating server instance from custom files...') - project_dir = self.ctx.config['app']['project_dir'] - + self.ctx.log["ut4"].info("Generating server instance from custom files...") + project_dir = self.ctx.config["app"]["project_dir"] + # rsync - src = '/'.join([project_dir, 'base/LinuxServer']) - dst = '/'.join([project_dir, 'instance/']) - cmd = 'rsync -ravzp {src} {dst}'.format(**{ - 'src': src, - 'dst': dst - }) + src = "/".join([project_dir, "base/LinuxServer"]) + dst = "/".join([project_dir, "instance/"]) + cmd = "rsync -ravzp {src} {dst}".format(**{"src": src, "dst": dst}) self._invoke_command(cmd) # cp 1 - src = '/'.join([project_dir, 'start-server.sh']) - dst = '/'.join([project_dir, 'instance/']) - cmd = 'cp {src} {dst}'.format(**{ - 'src': src, - 'dst': dst - }) + src = "/".join([project_dir, "start-server.sh"]) + dst = "/".join([project_dir, "instance/"]) + cmd = "cp {src} {dst}".format(**{"src": src, "dst": dst}) self._invoke_command(cmd) # cp 2 - src = '/'.join([project_dir, 'stop-server.sh']) - dst = '/'.join([project_dir, 'instance/']) - cmd = 'cp {src} {dst}'.format(**{ - 'src': src, - 'dst': dst - }) + src = "/".join([project_dir, "stop-server.sh"]) + dst = "/".join([project_dir, "instance/"]) + cmd = "cp {src} {dst}".format(**{"src": src, "dst": dst}) self._invoke_command(cmd) if self._needs_first_run(): @@ -149,35 +123,35 @@ ssh {remote_game_host} rm {remote_game_dir}/LinuxServer/UnrealTournament/Saved/L """ Flip on the target hub on for Fragging! """ - self.ctx.log['ut4'].info('Starting hub...') + self.ctx.log["ut4"].info("Starting hub...") - cmd = ''' + cmd = """ ssh {remote_game_host} {remote_game_dir}/start-server.sh -''' +""" self._invoke_command(cmd) def stop_server(self): """ Stop UT4 Hub processes on the server. """ - self.ctx.log['ut4'].info('Stopping hub.') - cmd = ''' + self.ctx.log["ut4"].info("Stopping hub.") + cmd = """ ssh {remote_game_host} {remote_game_dir}/stop-server.sh -''' +""" self._invoke_command(cmd) def upload_redirects(self): """ Upload paks to redirect server. """ - self.ctx.log['ut4'].info('Uploading redirects (maps, mutators, etc.) to target hub.') + self.ctx.log["ut4"].info("Uploading redirects (maps, mutators, etc.) to target hub.") - project_dir = self.ctx.config['app']['project_dir'] + project_dir = self.ctx.config["app"]["project_dir"] # paks_dir = os.path.join(project_dir, 'instance/LinuxServer/UnrealTournament/Content/Paks/') - paks_dir = os.path.join(project_dir, 'files/') # trailing slash required - remote_redirect_host = self.ctx.config['app']['remote_redirect_host'] + paks_dir = os.path.join(project_dir, "files/") # trailing slash required + remote_redirect_host = self.ctx.config["app"]["remote_redirect_host"] cwd = project_dir - cmd = ''' + cmd = """ rsync -rivz \ --delete \ --exclude "*.md5" \ @@ -186,11 +160,12 @@ rsync -rivz \ --exclude Mods.db \ {paks_dir} {remote_redirect_host} -'''\ -.format(**{ - 'paks_dir': paks_dir, - 'remote_redirect_host': remote_redirect_host, -}) +""".format( + **{ + "paks_dir": paks_dir, + "remote_redirect_host": remote_redirect_host, + } + ) # subprocess.run(cmd, cwd=cwd) # should be invoke_command? no because gui will need subprocess.run self._invoke_command(cmd) @@ -199,61 +174,66 @@ rsync -rivz \ self._redirect_chown() def _redirect_hide_passwords(self): - project_dir = self.ctx.config['app']['project_dir'] - remote_redirect_host = self.ctx.config['app']['remote_redirect_host'] + project_dir = self.ctx.config["app"]["project_dir"] + remote_redirect_host = self.ctx.config["app"]["remote_redirect_host"] # (on the server): - gameini="/srv/ut4-redirect.zavage.net/config/Game.ini" - engineini="/srv/ut4-redirect.zavage.net/config/Engine.ini" + gameini = "/srv/ut4-redirect.zavage.net/config/Game.ini" + engineini = "/srv/ut4-redirect.zavage.net/config/Engine.ini" - cmd = ''' + cmd = """ ssh mathewguest.com \ sed -i /ServerInstanceID=/c\ServerInstanceID=Hidden {gameini} -'''.format(gameini=gameini) +""".format( + gameini=gameini + ) self._invoke_command(cmd) - cmd = ''' + cmd = """ ssh mathewguest.com \ sed -i /RconPassword=/c\RconPassword=Hidden {engineini} -'''.format(engineini=engineini) +""".format( + engineini=engineini + ) self._invoke_command(cmd) - + def _redirect_upload_script(self): - project_dir = self.ctx.config['app']['project_dir'] - remote_redirect_host = self.ctx.config['app']['remote_redirect_host'] - cmd = ''' + project_dir = self.ctx.config["app"]["project_dir"] + remote_redirect_host = self.ctx.config["app"]["remote_redirect_host"] + cmd = """ rsync -vz \ {project_dir}/ut4-server-ctl.sh \ {remote_redirect_host} -'''\ -.format(**{ - 'project_dir': project_dir, - 'remote_redirect_host': remote_redirect_host, -}) +""".format( + **{ + "project_dir": project_dir, + "remote_redirect_host": remote_redirect_host, + } + ) # subprocess.run(cmd, cwd=cwd) # should be invoke_command? no because gui will need subprocess.run self._invoke_command(cmd) def _redirect_chown(self): - project_dir = self.ctx.config['app']['project_dir'] - remote_redirect_host = self.ctx.config['app']['remote_redirect_host'] + project_dir = self.ctx.config["app"]["project_dir"] + remote_redirect_host = self.ctx.config["app"]["remote_redirect_host"] - cmd = ''' + cmd = """ ssh mathewguest.com \ chown http:http /srv/ut4-redirect.zavage.net -R -''' +""" self._invoke_command(cmd) def upload_server(self): """ Upload all required game files to the hub server. """ - self.ctx.log['ut4'].info('Uploading customized server') - project_dir = self.ctx.config['app']['project_dir'] - remote_game_host = self.ctx.config['app']['remote_game_host'] - remote_game_dir = self.ctx.config['app']['remote_game_dir'] + self.ctx.log["ut4"].info("Uploading customized server") + project_dir = self.ctx.config["app"]["project_dir"] + remote_game_host = self.ctx.config["app"]["remote_game_host"] + remote_game_dir = self.ctx.config["app"]["remote_game_dir"] cwd = None # transfer #1 - cmd = ''' + cmd = """ rsync -raivzp \ --delete \ --exclude ".KEEP" \ @@ -266,88 +246,72 @@ rsync -raivzp \ --exclude "Saved/Logs/*" \ {project_dir}/instance/ \ {remote_game_host}:{remote_game_dir} -'''\ -.format(**{ - 'project_dir': project_dir, - 'remote_game_host': remote_game_host, - 'remote_game_dir': remote_game_dir -}) - cmd = cmd.replace(' ', '') +""".format( + **{"project_dir": project_dir, "remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir} + ) + cmd = cmd.replace(" ", "") # subprocess.run(cmd, cwd=cwd) # should be invoke_command? no because gui will need subprocess.run self._invoke_command(cmd) # transfer #2 - cmd = ''' + cmd = """ rsync -avzp \ {project_dir}/ut4-server-ctl.sh \ {remote_game_host}:{remote_game_dir} -'''\ -.format(**{ - 'project_dir': project_dir, - 'remote_game_host': remote_game_host, - 'remote_game_dir': remote_game_dir -}) +""".format( + **{"project_dir": project_dir, "remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir} + ) # subprocess.run(cmd, cwd=cwd) self._invoke_command(cmd) # transfer #3 - cmd = ''' + cmd = """ scp \ {project_dir}/instance/ut4-server.service \ {remote_game_host}:/etc/systemd/system/ -'''\ -.format(**{ - 'project_dir': project_dir, - 'remote_game_host': remote_game_host, - 'remote_game_dir': remote_game_dir -}) +""".format( + **{"project_dir": project_dir, "remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir} + ) # subprocess.run(cmd, cwd=cwd) self._invoke_command(cmd) # transfer #4 - cmd = ''' + cmd = """ ssh {remote_game_host} \ chown ut4:ut4 {remote_game_dir} -R -'''.format(**{ - 'remote_game_host': remote_game_host, - 'remote_game_dir': remote_game_dir - -}) +""".format( + **{"remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir} + ) # subprocess.run(cmd, cwd=cwd) self._invoke_command(cmd) - - # Fix +x permissions on bash scripts - cmd = ''' + cmd = """ ssh {remote_game_host} \ chmod +x \ {remote_game_dir}/start-server.sh \ {remote_game_dir}/stop-server.sh -'''.format(**{ - 'remote_game_host': remote_game_host, - 'remote_game_dir': remote_game_dir - -}) +""".format( + **{"remote_game_host": remote_game_host, "remote_game_dir": remote_game_dir} + ) # subprocess.run(cmd, cwd=cwd) self._invoke_command(cmd) def _first_run(self): - self.ctx.log['ut4'].info('Starting instance once to get UID.') - self.ctx.log['ut4'].info('Unfortunately, this takes 20 seconds. Just wait.') + self.ctx.log["ut4"].info("Starting instance once to get UID.") + self.ctx.log["ut4"].info("Unfortunately, this takes 20 seconds. Just wait.") - # Make binary executable: - bin_name = 'UE4Server-Linux-Shipping' - project_dir = self.ctx.config['app']['project_dir'] + # Make binary executable: + bin_name = "UE4Server-Linux-Shipping" + project_dir = self.ctx.config["app"]["project_dir"] - cwd = '{project_dir}/instance/LinuxServer/Engine/Binaries/Linux'\ - .format(project_dir=project_dir) - target_file = '{cwd}/{bin_name}'.format(cwd=cwd, bin_name=bin_name) - cmd = ['chmod', '770', target_file] + cwd = "{project_dir}/instance/LinuxServer/Engine/Binaries/Linux".format(project_dir=project_dir) + target_file = "{cwd}/{bin_name}".format(cwd=cwd, bin_name=bin_name) + cmd = ["chmod", "770", target_file] p = subprocess.run(cmd) - cmd = ['./'+bin_name, 'UnrealTournament', 'UT-Entry?Game=Lobby', '-log'] + cmd = ["./" + bin_name, "UnrealTournament", "UT-Entry?Game=Lobby", "-log"] p = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE) try: @@ -356,68 +320,68 @@ ssh {remote_game_host} \ p.kill() stdout, stderr = p.communicate() - self.ctx.log['ut4'].info('sleeping 20 seconds and then we\'ll kill the server we started just now.') + self.ctx.log["ut4"].info("sleeping 20 seconds and then we'll kill the server we started just now.") # # TODO(MG) get uid and export def _install_config(self): - files = ( - 'Game.ini', - 'Engine.ini' - ) - project_dir = self.ctx.config['app']['project_dir'] - config_dir = self.ctx.config['app']['config_dir'] + files = ("Game.ini", "Engine.ini") + project_dir = self.ctx.config["app"]["project_dir"] + config_dir = self.ctx.config["app"]["config_dir"] for fn in files: - self.ctx.log['ut4'].info('Installing file: %s', fn) + self.ctx.log["ut4"].info("Installing file: %s", fn) src = os.path.join(config_dir, fn) - dst = os.path.join(project_dir, 'instance/LinuxServer/UnrealTournament/Saved/Config/LinuxServer', fn) - cmd = 'cp {src} {dst}'.format(**{ - 'src': src, - 'dst': dst - }) + dst = os.path.join(project_dir, "instance/LinuxServer/UnrealTournament/Saved/Config/LinuxServer", fn) + cmd = "cp {src} {dst}".format(**{"src": src, "dst": dst}) self._invoke_command(cmd) # Monkey-patch Game.ini to ensure it has a place for RedirectReferences - if fn == 'Game.ini': + if fn == "Game.ini": ini = UnrealIniFile(dst) - sect_name = '/Script/UnrealTournament.UTBaseGameMode' - opt_name = 'RedirectReferences' + sect_name = "/Script/UnrealTournament.UTBaseGameMode" + opt_name = "RedirectReferences" if not ini._config.has_section(sect_name): ini._config.add_section(sect_name) if not ini._config.has_option(sect_name, opt_name): - ini._config.set(sect_name, opt_name, ':PARAM:') - with open(dst, 'w') as fp: + ini._config.set(sect_name, opt_name, ":PARAM:") + with open(dst, "w") as fp: ini._config.write(fp) def _install_paks(self): - project_dir = self.ctx.config['app']['project_dir'] + project_dir = self.ctx.config["app"]["project_dir"] - self.ctx.log['ut4'].info('Installing maps...') - cmd = 'rsync -ravzp {src} {dst}'.format(**{ - 'src': '/'.join([project_dir, 'files/maps/']), - 'dst': '/'.join([project_dir, 'instance/LinuxServer/UnrealTournament/Content/Paks/']) - }) + self.ctx.log["ut4"].info("Installing maps...") + cmd = "rsync -ravzp {src} {dst}".format( + **{ + "src": "/".join([project_dir, "files/maps/"]), + "dst": "/".join([project_dir, "instance/LinuxServer/UnrealTournament/Content/Paks/"]), + } + ) self._invoke_command(cmd) - self.ctx.log['ut4'].info('Installing mutators...') - cmd = 'rsync -ravzp {src} {dst}'.format(**{ - 'src': '/'.join([project_dir, 'files/mutators/']), - 'dst': '/'.join([project_dir, 'instance/LinuxServer/UnrealTournament/Content/Paks/']) - }) + self.ctx.log["ut4"].info("Installing mutators...") + cmd = "rsync -ravzp {src} {dst}".format( + **{ + "src": "/".join([project_dir, "files/mutators/"]), + "dst": "/".join([project_dir, "instance/LinuxServer/UnrealTournament/Content/Paks/"]), + } + ) self._invoke_command(cmd) def _install_redirect_lines(self): - self.ctx.log['ut4'].info('Generating redirect references...') + self.ctx.log["ut4"].info("Generating redirect references...") - redirect_protocol = self.ctx.config['app']['redirect_protocol'] - redirect_url = self.ctx.config['app']['redirect_url'] - project_dir = self.ctx.config['app']['project_dir'] - mod_dir = '/'.join([project_dir, 'files']) + redirect_protocol = self.ctx.config["app"]["redirect_protocol"] + redirect_url = self.ctx.config["app"]["redirect_url"] + project_dir = self.ctx.config["app"]["project_dir"] + mod_dir = "/".join([project_dir, "files"]) - game_ini_filepath = '/'.join([project_dir, 'instance/LinuxServer/UnrealTournament/Saved/Config/LinuxServer/Game.ini']) + game_ini_filepath = "/".join( + [project_dir, "instance/LinuxServer/UnrealTournament/Saved/Config/LinuxServer/Game.ini"] + ) game_ini = GameIniSpecial(game_ini_filepath) game_ini.clear_redirect_references() - files = glob.glob('{}/**/*.pak'.format(mod_dir)) + files = glob.glob("{}/**/*.pak".format(mod_dir)) redirect_lines = [] for idx, filename in enumerate(files): # if idx > 5: @@ -435,90 +399,85 @@ ssh {remote_game_host} \ print(ex) continue - line = game_ini.add_redirect_reference(**{ - 'pkg_basename': pkg_basename, - 'redirect_protocol': redirect_protocol, - 'redirect_url': redirect_url, - 'relative_path': relative_path, - 'md5sum': md5sum - }) + line = game_ini.add_redirect_reference( + **{ + "pkg_basename": pkg_basename, + "redirect_protocol": redirect_protocol, + "redirect_url": redirect_url, + "relative_path": relative_path, + "md5sum": md5sum, + } + ) - self.ctx.log['ut4'].debug("redirect line = '%s'", line) + self.ctx.log["ut4"].debug("redirect line = '%s'", line) data = game_ini.write(sys.stdout) def _install_rulesets(self): - self.ctx.log['ut4'].info('Concatenating rulesets for game modes...') - project_dir = self.ctx.config['app']['project_dir'] + self.ctx.log["ut4"].info("Concatenating rulesets for game modes...") + project_dir = self.ctx.config["app"]["project_dir"] - src_dir = '/'.join([project_dir, 'files/rulesets']) - out_dir = '/'.join([project_dir, '/instance/LinuxServer/UnrealTournament/Saved/Config/Rulesets']) - out_filename='/'.join([out_dir, 'ruleset.json']) + src_dir = "/".join([project_dir, "files/rulesets"]) + out_dir = "/".join([project_dir, "/instance/LinuxServer/UnrealTournament/Saved/Config/Rulesets"]) + out_filename = "/".join([out_dir, "ruleset.json"]) - cmd = 'mkdir -pv {out_dir}'.format(**{ - 'out_dir': out_dir - }) + cmd = "mkdir -pv {out_dir}".format(**{"out_dir": out_dir}) self._invoke_command(cmd) - self.ctx.log['ut4'].info('out filename=%s', out_filename) - + self.ctx.log["ut4"].info("out filename=%s", out_filename) + # echo {\"rules\":[ > "$OUT_FILENAME" - cmd = "echo '{{\"rules\":[' > \"{out_filename}\"".format(out_filename=out_filename) + cmd = 'echo \'{{"rules":[\' > "{out_filename}"'.format(out_filename=out_filename) self._invoke_command(cmd) cmd = 'for f in "{src_dir}"/*.json ; do cat "$f" >> "{out_filename}" ; done'.format( - **{ - 'src_dir': src_dir, - 'out_filename': out_filename - }) + **{"src_dir": src_dir, "out_filename": out_filename} + ) self._invoke_command(cmd) - - cmd = 'echo "]}}" >> "{out_filename}"'.format( - **{ - 'out_filename': out_filename - }) + + cmd = 'echo "]}}" >> "{out_filename}"'.format(**{"out_filename": out_filename}) self._invoke_command(cmd) - self.ctx.log['ut4'].info('output ruleset is at "%s"', out_filename) - + self.ctx.log["ut4"].info('output ruleset is at "%s"', out_filename) + def _invoke_command(self, cmd, msg=None): - assert isinstance(cmd, str), 'cmd input must be string: %s'.format(cmd) + assert isinstance(cmd, str), "cmd input must be string: %s".format(cmd) if msg is None: msg = cmd print(msg) - self.ctx.log['ut4'].info('running cmd: %s', cmd) - cwd = None # todo(mg) ? + self.ctx.log["ut4"].info("running cmd: %s", cmd) + cwd = None # todo(mg) ? os.system(cmd) def _needs_first_run(self): - return False # TODO(MG): Hard-coded + return False # TODO(MG): Hard-coded def _validate_env_vars(self): variable_names = ( - 'project_dir', - 'download_url', - 'download_filename', - 'download_md5', - 'redirect_protocol', - 'redirect_url', - 'remote_game_host', - 'remote_game_dir', - 'remote_redirect_host' + "project_dir", + "download_url", + "download_filename", + "download_md5", + "redirect_protocol", + "redirect_url", + "remote_game_host", + "remote_game_dir", + "remote_redirect_host", ) for name in variable_names: - value = self.ctx.config['app'][name] - self.ctx.log['ut4'].info('%s: %s', name, value) + value = self.ctx.config["app"][name] + self.ctx.log["ut4"].info("%s: %s", name, value) - i = input('Continue with above configuration? (y/N):') - if i.lower() != 'y': - self.ctx.log['ut4'].info('Doing nothing.') + i = input("Continue with above configuration? (y/N):") + if i.lower() != "y": + self.ctx.log["ut4"].info("Doing nothing.") return False - self.ctx.log['ut4'].info('Continuing.') + self.ctx.log["ut4"].info("Continuing.") return True + class MultiOrderedDict(collections.OrderedDict): def __setitem__(self, key, value): if isinstance(value, list) and key in self: self[key].extend(value) else: super().__setitem__(key, value) - diff --git a/smileyface/myutil.py b/smileyface/myutil.py index 65cc760..8138392 100644 --- a/smileyface/myutil.py +++ b/smileyface/myutil.py @@ -5,14 +5,14 @@ import os def ensure_dir_exists(dirpath): if dirpath is None: return - if dirpath == '': + if dirpath == "": return dirpath = os.path.dirname(dirpath) os.makedirs(dirpath, exist_ok=True) def md5_file(filename): - with open(filename, 'rb') as fp: + with open(filename, "rb") as fp: data = fp.read() h = hashlib.md5(data).hexdigest() - return h \ No newline at end of file + return h diff --git a/smileyface/scrape_latest/__init__.py b/smileyface/scrape_latest/__init__.py index 9bed84c..d8248b0 100644 --- a/smileyface/scrape_latest/__init__.py +++ b/smileyface/scrape_latest/__init__.py @@ -1,3 +1,3 @@ from .local_fs import * -from .scrape_utcc import * from .scrape_ut4pugs import * +from .scrape_utcc import * diff --git a/smileyface/scrape_latest/local_fs.py b/smileyface/scrape_latest/local_fs.py index 8865906..dfe1b27 100644 --- a/smileyface/scrape_latest/local_fs.py +++ b/smileyface/scrape_latest/local_fs.py @@ -1,9 +1,8 @@ -from smileyface import myutil -from smileyface import structs - import datetime import os +from smileyface import myutil, structs + class LocalFs: def __init__(self, ctx, datalayer): @@ -14,12 +13,12 @@ class LocalFs: self.datalayer.create_tables() def load_md5s(self): - paks_dir = self.ctx.config['app']['project_dir'] - maps_dir = os.path.join(paks_dir, 'files', 'maps') + paks_dir = self.ctx.config["app"]["project_dir"] + maps_dir = os.path.join(paks_dir, "files", "maps") print(maps_dir) self._load_md5_one_dir(maps_dir) - muts_dir = os.path.join(paks_dir, 'files', 'mutators') + muts_dir = os.path.join(paks_dir, "files", "mutators") print(muts_dir) self._load_md5_one_dir(muts_dir) diff --git a/smileyface/scrape_latest/scrape_ut4pugs.py b/smileyface/scrape_latest/scrape_ut4pugs.py index c2de8d2..94493c1 100644 --- a/smileyface/scrape_latest/scrape_ut4pugs.py +++ b/smileyface/scrape_latest/scrape_ut4pugs.py @@ -1,8 +1,8 @@ import selenium import selenium.webdriver -URL_MUTATORS = 'https://ut4pugs.us/redirect-mutators' -URL_MAPS = 'https://ut4pugs.us/redirect-mutators' +URL_MUTATORS = "https://ut4pugs.us/redirect-mutators" +URL_MAPS = "https://ut4pugs.us/redirect-mutators" class ScrapeUt4Pugs: @@ -25,14 +25,14 @@ class ScrapeUt4Pugs: self._check_pak_md5sums() def _check_pak_md5sums(self): - tbl_of_mutators = self.browser.find_element_by_id('myTable') + tbl_of_mutators = self.browser.find_element_by_id("myTable") print(tbl_of_mutators) - mut_rows = tbl_of_mutators.find_elements_by_xpath('tbody/tr') + mut_rows = tbl_of_mutators.find_elements_by_xpath("tbody/tr") for r in mut_rows: - mut_cols = r.find_elements_by_xpath('td') + mut_cols = r.find_elements_by_xpath("td") if len(mut_cols) != 3: - input(' at unexpected columns for mutator. received {}'.format(len(mut_cols))) + input(" at unexpected columns for mutator. received {}".format(len(mut_cols))) mut_file = mut_cols[0] mut_md5 = mut_cols[1] mut_ini_line = mut_cols[2] @@ -44,22 +44,22 @@ class ScrapeUt4Pugs: local_file = self.datalayer.query_filepak(mut_file.text) print(local_file) if not local_file: - self.ctx.log['ut4'].warn('pak not found locally: %s', mut_file.text) + self.ctx.log["ut4"].warn("pak not found locally: %s", mut_file.text) continue local_md5 = local_file.md5sum remote_md5 = mut_md5.text if local_md5 != remote_md5: - input(' as mismatching md5!') - print('local: ', local_md5) - print('remote: ', remote_md5) - self.datalayer.mark_filepak_validated_state(local_file.file_pak_id, 'mismatch', 'ut4pugs', remote_md5) + input(" as mismatching md5!") + print("local: ", local_md5) + print("remote: ", remote_md5) + self.datalayer.mark_filepak_validated_state(local_file.file_pak_id, "mismatch", "ut4pugs", remote_md5) else: - input(' as matching md5! good job') - self.datalayer.mark_filepak_validated_state(local_file.file_pak_id, 'valid', 'ut4pugs', None) + input(" as matching md5! good job") + self.datalayer.mark_filepak_validated_state(local_file.file_pak_id, "valid", "ut4pugs", None) # print(r) - print('xxx') + print("xxx") pass pass diff --git a/smileyface/scrape_latest/scrape_utcc.py b/smileyface/scrape_latest/scrape_utcc.py index 4d5564a..bef527e 100644 --- a/smileyface/scrape_latest/scrape_utcc.py +++ b/smileyface/scrape_latest/scrape_utcc.py @@ -1,7 +1,7 @@ import selenium import selenium.webdriver -URL_CARDS_LIST = 'https://utcc.unrealpugs.com/content' +URL_CARDS_LIST = "https://utcc.unrealpugs.com/content" class ScrapeUtcc: @@ -21,4 +21,3 @@ class ScrapeUtcc: def _scrape_list_of_content_cards(self): self.browser.get(URL_CARDS_LIST) - diff --git a/smileyface/structs.py b/smileyface/structs.py index 55072b8..a091dd1 100644 --- a/smileyface/structs.py +++ b/smileyface/structs.py @@ -8,4 +8,4 @@ class FilePak: self.filename = None self.md5sum = None self.record_created_at = None - self.record_updated_at = None \ No newline at end of file + self.record_updated_at = None