diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0449207..fdd8f20 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,6 +20,9 @@ jobs: - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + - name: Install system dependencies + run: sudo apt-get install libgtk-3-dev + if: ${{ matrix.os == 'ubuntu-latest' }} - name: Run image uses: abatilo/actions-poetry@v2 with: @@ -29,11 +32,8 @@ jobs: - name: Install dependencies run: poetry install - - - name: install pyinstaller - run: poetry run pip install -U pyinstaller - name: Build execution file - run: poetry run pyinstaller main.py --onefile + run: poetry run pyinstaller main.py --onefile --windowed - name: Sign the macos-version build if: ${{ matrix.os == 'macos-latest' }} diff --git a/README.md b/README.md new file mode 100644 index 0000000..9fb05e7 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Feedback data converter + +Converts feedback data that has been collected from courses.mooc.fi exercises to an Excel file. + +## Usage + +1. Download the program from the releases on the right side of this page. +2. Create a new empty folder where you'll place the program. The program, the input files and the output files will be placed in this folder. +3. Run the program once to create the configuration file on the same folder. + - If you're running Linux, you'll need to allow executing the file as a program. (Right click the file, select Properties, go to Permissions tab and check the "Allow executing file as program" checkbox.) +4. Export exercise tasks and submissions from courses.mooc.fi and place them to a folder called `data`. +5. Run the program. The output file will be placed in a folder called `output`. + diff --git a/main.py b/main.py index bcb35c8..17dac3c 100644 --- a/main.py +++ b/main.py @@ -9,6 +9,7 @@ import numpy as np import pdb import sys +from argparse import ArgumentParser current_file_path = sys.executable basename = os.path.basename(current_file_path) @@ -18,8 +19,12 @@ def main(): - # Change pwd to the directory of this file + # To make Gooey work, we need to use argparse + parser = ArgumentParser(add_help=True) + args = parser.parse_args() + print("🚀 Starting the program") + # Change pwd to the directory of this file abspath = os.path.abspath(current_file_path) dname = os.path.dirname(abspath) print(f"Using working directory {dname}") @@ -202,9 +207,8 @@ def sort_key(x): print() current_timestamp_in_iso_format = date.today().isoformat() - workbook = xlsxwriter.Workbook( - f"./output/feedback_{current_timestamp_in_iso_format}.xlsx" - ) + excel_output_path = f"./output/feedback_{current_timestamp_in_iso_format}.xlsx" + workbook = xlsxwriter.Workbook(excel_output_path) # create output folder if it does not exist if not os.path.exists("./output"): @@ -265,7 +269,25 @@ def sort_key(x): ) workbook.close() + print() + print(f"✅ Processing complete. Results written to {excel_output_path}") if __name__ == "__main__": + if "--help" in sys.argv or "-h" in sys.argv: + print("Note: To run this program with a graphical user interface, use --gui") + # If we are not running from the terminal, run with GUI + run_with_gui = not sys.stdin.isatty() + if "--gui" in sys.argv: + run_with_gui = True + if run_with_gui: + from gooey import Gooey + + main = Gooey( + auto_start=True, + program_name="Feedback data converter", + # make the window a little bigger + default_size=(800, 900), + terminal_font_family="monospace", + )(main) main() diff --git a/poetry.lock b/poetry.lock index 00d2dac..549c829 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,11 @@ +[[package]] +name = "altgraph" +version = "0.17.4" +description = "Python graph (network) package" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "appnope" version = "0.1.3" @@ -47,6 +55,14 @@ category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +[[package]] +name = "colored" +version = "2.2.3" +description = "Simple python library for color and formatting to terminal" +category = "main" +optional = false +python-versions = ">=3.9" + [[package]] name = "comm" version = "0.1.4" @@ -101,6 +117,21 @@ python-versions = "*" [package.extras] tests = ["asttokens", "pytest", "littleutils", "rich"] +[[package]] +name = "gooey" +version = "1.0.8.1" +description = "Turn (almost) any command line program into a full GUI application with one line" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +colored = ">=1.3.93" +Pillow = ">=4.3.0" +psutil = ">=5.4.2" +pygtrie = ">=2.3.3" +wxpython = ">=4.1.0" + [[package]] name = "ipykernel" version = "6.25.2" @@ -219,6 +250,17 @@ traitlets = ">=5.3" docs = ["myst-parser", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] +[[package]] +name = "macholib" +version = "1.16.3" +description = "Mach-O header analysis and editing" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +altgraph = ">=0.17" + [[package]] name = "matplotlib-inline" version = "0.1.6" @@ -266,6 +308,14 @@ python-versions = ">=3.6" qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] testing = ["docopt", "pytest (<6.0.0)"] +[[package]] +name = "pefile" +version = "2023.2.7" +description = "Python PE parsing module" +category = "dev" +optional = false +python-versions = ">=3.6.0" + [[package]] name = "pexpect" version = "4.8.0" @@ -285,6 +335,18 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "pillow" +version = "10.0.1" +description = "Python Imaging Library (Fork)" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + [[package]] name = "platformdirs" version = "3.10.0" @@ -339,7 +401,7 @@ wcwidth = "*" name = "psutil" version = "5.9.5" description = "Cross-platform lib for process and system monitoring in Python." -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" @@ -384,6 +446,41 @@ python-versions = ">=3.7" [package.extras] plugins = ["importlib-metadata"] +[[package]] +name = "pygtrie" +version = "2.5.0" +description = "A pure Python trie data structure implementation." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pyinstaller" +version = "6.0.0" +description = "PyInstaller bundles a Python application and all its dependencies into a single package." +category = "dev" +optional = false +python-versions = "<3.13,>=3.8" + +[package.dependencies] +altgraph = "*" +macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} +packaging = ">=20.0" +pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""} +pyinstaller-hooks-contrib = ">=2021.4" +pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""} + +[package.extras] +hook_testing = ["pytest (>=2.7.3)", "execnet (>=1.5.0)", "psutil"] + +[[package]] +name = "pyinstaller-hooks-contrib" +version = "2023.9" +description = "Community maintained hooks for PyInstaller" +category = "dev" +optional = false +python-versions = ">=3.7" + [[package]] name = "python-dateutil" version = "2.8.2" @@ -403,6 +500,14 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "pywin32-ctypes" +version = "0.2.2" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "pyyaml" version = "6.0.1" @@ -426,7 +531,7 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" @@ -474,6 +579,19 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "wxpython" +version = "4.2.1" +description = "Cross platform GUI toolkit for Python, \"Phoenix\" version" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +numpy = {version = "*", markers = "python_version >= \"3.0\" and python_version < \"3.12\""} +pillow = "*" +six = "*" + [[package]] name = "xlsxwriter" version = "3.1.4" @@ -485,31 +603,37 @@ python-versions = ">=3.6" [metadata] lock-version = "1.1" python-versions = ">=3.10,<3.13" -content-hash = "67f55fe61691a6710698ece71d05769fdc88a72ec379a1b89d89805e2671e0e1" +content-hash = "fa6400eec0dea97af32926891c84e38467bfb9daa22405307e387dc31a0797de" [metadata.files] +altgraph = [] appnope = [] asttokens = [] backcall = [] cffi = [] colorama = [] +colored = [] comm = [] debugpy = [] decorator = [] exceptiongroup = [] executing = [] +gooey = [] ipykernel = [] ipython = [] jedi = [] jupyter-client = [] jupyter-core = [] +macholib = [] matplotlib-inline = [] nest-asyncio = [] numpy = [] packaging = [] parso = [] +pefile = [] pexpect = [] pickleshare = [] +pillow = [] platformdirs = [] polars = [] prompt-toolkit = [] @@ -518,8 +642,12 @@ ptyprocess = [] pure-eval = [] pycparser = [] pygments = [] +pygtrie = [] +pyinstaller = [] +pyinstaller-hooks-contrib = [] python-dateutil = [] pywin32 = [] +pywin32-ctypes = [] pyyaml = [] pyzmq = [] six = [] @@ -527,4 +655,5 @@ stack-data = [] tornado = [] traitlets = [] wcwidth = [] +wxpython = [] xlsxwriter = [] diff --git a/pyproject.toml b/pyproject.toml index 608dd2b..614076a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,9 +11,11 @@ polars = "^0.19.3" PyYAML = "^6.0.1" XlsxWriter = "^3.1.4" numpy = "^1.26.0" +Gooey = "^1.0.8" [tool.poetry.dev-dependencies] ipykernel = "^6.25.2" +pyinstaller = "^6.0.0" [build-system] requires = ["poetry-core>=1.0.0"]