diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e1774d --- /dev/null +++ b/.gitignore @@ -0,0 +1,105 @@ +# See https://github.com/github/gitignore/blob/master/Python.gitignore +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..15c9d72 --- /dev/null +++ b/Pipfile @@ -0,0 +1,19 @@ +[[source]] +url = "https://pypi.python.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +pdf = "*" +PyPDF2 = "*" +Reportlab = "*" +nameparser = "*" +PyYaml = "*" +pyyaml = "*" +"e1839a8" = {path = ".", editable = true} + +[dev-packages] +"e1839a8" = {path = ".", editable = true} + +[requires] +python_version = "3.6" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..4f327db --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,149 @@ +{ + "_meta": { + "hash": { + "sha256": "04c565b34f1c8df3b5f592e711f175d26b9edf3d7617b1ea084666e356aa9154" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.6" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "e1839a8": { + "editable": true, + "path": "." + }, + "nameparser": { + "hashes": [ + "sha256:9187138d3febe3f1f5c80f7d7addaaa329e6ecfe4dd71fc8082020b6862fc9aa", + "sha256:dd14dc660410e1458bacb706ed1aa9d0d4dbada4909953c287c033bdc6426bed" + ], + "index": "pypi", + "version": "==0.5.6" + }, + "pdf": { + "hashes": [ + "sha256:938d24c647168065f7494bc2159898d7ff0660363bfe4bf2874d6cb302556291" + ], + "index": "pypi", + "version": "==1.0" + }, + "pillow": { + "hashes": [ + "sha256:00633bc2ec40313f4daf351855e506d296ec3c553f21b66720d0f1225ca84c6f", + "sha256:03514478db61b034fc5d38b9bf060f994e5916776e93f02e59732a8270069c61", + "sha256:040144ba422216aecf7577484865ade90e1a475f867301c48bf9fbd7579efd76", + "sha256:16246261ff22368e5e32ad74d5ef40403ab6895171a7fc6d34f6c17cfc0f1943", + "sha256:1cb38df69362af35c14d4a50123b63c7ff18ec9a6d4d5da629a6f19d05e16ba8", + "sha256:2400e122f7b21d9801798207e424cbe1f716cee7314cd0c8963fdb6fc564b5fb", + "sha256:2ee6364b270b56a49e8b8a51488e847ab130adc1220c171bed6818c0d4742455", + "sha256:3b4560c3891b05022c464b09121bd507c477505a4e19d703e1027a3a7c68d896", + "sha256:41374a6afb3f44794410dab54a0d7175e6209a5a02d407119c81083f1a4c1841", + "sha256:438a3faf5f702c8d0f80b9f9f9b8382cfa048ca6a0d64ef71b86b563b0ee0359", + "sha256:472a124c640bde4d5468f6991c9fa7e30b723d84ac4195a77c6ab6aea30f2b9c", + "sha256:4d32c8e3623a61d6e29ccd024066cd1ba556555abfb4cd714155020e00107e3f", + "sha256:4d8077fd649ac40a5c4165f2c22fa2a4ad18c668e271ecb2f9d849d1017a9313", + "sha256:62ec7ae98357fcd46002c110bb7cad15fce532776f0cbe7ca1d44c49b837d49d", + "sha256:6c7cab6a05351cf61e469937c49dbf3cdf5ffb3eeac71f8d22dc9be3507598d8", + "sha256:6eca36905444c4b91fe61f1b9933a47a30480738a1dd26501ff67d94fc2bc112", + "sha256:74e2ebfd19c16c28ad43b8a28ff73b904ed382ea4875188838541751986e8c9a", + "sha256:7673e7473a13107059377c96c563aa36f73184c29d2926882e0a0210b779a1e7", + "sha256:81762cf5fca9a82b53b7b2d0e6b420e0f3b06167b97678c81d00470daa622d58", + "sha256:8554bbeb4218d9cfb1917c69e6f2d2ad0be9b18a775d2162547edf992e1f5f1f", + "sha256:9b66e968da9c4393f5795285528bc862c7b97b91251f31a08004a3c626d18114", + "sha256:a00edb2dec0035e98ac3ec768086f0b06dfabb4ad308592ede364ef573692f55", + "sha256:b48401752496757e95304a46213c3155bc911ac884bed2e9b275ce1c1df3e293", + "sha256:b6cf18f9e653a8077522bb3aa753a776b117e3e0cc872c25811cfdf1459491c2", + "sha256:bb8adab1877e9213385cbb1adc297ed8337e01872c42a30cfaa66ff8c422779c", + "sha256:c8a4b39ba380b57a31a4b5449a9d257b1302d8bc4799767e645dcee25725efe1", + "sha256:cee9bc75bff455d317b6947081df0824a8f118de2786dc3d74a3503fd631f4ef", + "sha256:d0dc1313dff48af64517cbbd85e046d6b477fbe5e9d69712801f024dcb08c62b", + "sha256:d5bf527ed83617edd1855a5c923eeeaf68bcb9ac0ceb28e3f19b575b3a424984", + "sha256:df5863a21f91de5ecdf7d32a32f406dd9867ebb35d41033b8bd9607a21887599", + "sha256:e39142332541ed2884c257495504858b22c078a5d781059b07aba4c3a80d7551", + "sha256:e52e8f675ba0b2b417fa98579e7286a41a8e23871f17f4793772f5aa884fea79", + "sha256:e6dd55d5d94b9e36929325dd0c9ab85bfde84a5fc35947c334c32af1af668944", + "sha256:e87cc1acbebf263f308a8494272c2d42016aa33c32bf14d209c81e1f65e11868", + "sha256:ea0091cd4100519cedfeea2c659f52291f535ac6725e2368bcf59e874f270efa", + "sha256:eeb247f4f4d962942b3b555530b0c63b77473c7bfe475e51c6b75b7344b49ce3", + "sha256:f0d4433adce6075efd24fc0285135248b0b50f5a58129c7e552030e04fe45c7f", + "sha256:f1f3bd92f8e12dc22884935a73c9f94c4d9bd0d34410c456540713d6b7832b8c", + "sha256:f42a87cbf50e905f49f053c0b1fb86c911c730624022bf44c8857244fc4cdaca", + "sha256:f5f302db65e2e0ae96e26670818157640d3ca83a3054c290eff3631598dcf819", + "sha256:f7634d534662bbb08976db801ba27a112aee23e597eeaf09267b4575341e45bf", + "sha256:fdd374c02e8bb2d6468a85be50ea66e1c4ef9e809974c30d8576728473a6ed03", + "sha256:fe6931db24716a0845bd8c8915bd096b77c2a7043e6fc59ae9ca364fe816f08b" + ], + "version": "==5.1.0" + }, + "pypdf2": { + "hashes": [ + "sha256:e28f902f2f0a1603ea95ebe21dff311ef09be3d0f0ef29a3e44a932729564385" + ], + "index": "pypi", + "version": "==1.26.0" + }, + "pyyaml": { + "hashes": [ + "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8", + "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736", + "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f", + "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608", + "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8", + "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab", + "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7", + "sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3", + "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1", + "sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6", + "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8", + "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4", + "sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca", + "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269" + ], + "index": "pypi", + "version": "==3.12" + }, + "reportlab": { + "hashes": [ + "sha256:0474d275d83d850f24e3ccdacab1e81393cd43504a30808651ea909e52792624", + "sha256:108b59f384bcdeb64147be778c700083cc5e81d1e15d7ab1980c92f177421087", + "sha256:2d39db61b6d7d003e10bd170698fffef3e12b2ed86e14e3e57112ac448e9d8cb", + "sha256:3adaca93567100236ecf0027f4fca5738cc26cc333438853d3d5f3f88d024b6f", + "sha256:539fb522fb5717f7c11388f474c1cfb2958b4b94774eecffab7a40eeb8ba797c", + "sha256:554438f00e83b16302f84db06f3b18b0ebef85c57becc873155f388cdd22e2b5", + "sha256:582cf2acc6665e082bf7b2d9a78c6217a54fc666679d413c32b9c8f43ba6ee26", + "sha256:5beaf35e59dfd5ebd814fdefd76908292e818c982bd7332b5d347dfd2f01c343", + "sha256:64bb24267ae4f35436f04913fcffb84f9f40d86545cc1dcce78df0f49451d0af", + "sha256:6b019655ec42ea1fb7e1ae2017de5a6ad5ce3ed74e510fa4bbcba2afe3e836a4", + "sha256:7198c012a6e706dbae349a36f2e599a4a1a01e37cbfc86fb137d79614c5a5011", + "sha256:775259f56d9ec8715c64542fa3504d8ccb37dab7e0814a1949895f8f1412198c", + "sha256:86cb27afdee2e767255c2563402e64a6e0b15c3d4fc6b270a7ed79bad7582b8e", + "sha256:917c38d256422d3a8d17cff3de05e425c3ffeb6d535174ce9b2c7d04ce5eadd1", + "sha256:98e8d49597b2611b4cfe8c4c80abe7885c1e0dac2b458f4ca4b4d8ce58319815", + "sha256:a71b183f34a55bbf127ef9b30a00400559fcb68d677bb63c9eda1094a09c2b01", + "sha256:a7c38d4dbfaea4685fb40bdf8a35caee94633ab66ffa951c845c78d3b288a693", + "sha256:ab610aa2e80051d54eca8f8fb1f00fc995624dc5a44c10a4740397b66aedf915", + "sha256:b2931888c135151f1eaacfccd7df3a7701404c2741470f75827f01e6a576c2d3", + "sha256:cdf8df8123b510b9ad3ec7f80e83fc7dbc4c16640264080966789190b27af04e", + "sha256:d54b1a4ab3ac016dd0fc5ffb495ce8a174e0601d9f8ff9e023924c661400b629", + "sha256:fa5d0f881d107d7e7528f16462a19419f8da1da9ceb801974f05fd2d211f336d", + "sha256:fdc6d44d7881259bb2bc7d85fea5d6e4965844f94391b07e5b3c899a9b5c2baa" + ], + "index": "pypi", + "version": "==3.4.0" + } + }, + "develop": { + "e1839a8": { + "editable": true, + "path": "." + } + } +} diff --git a/README.md b/README.md index 134c3b1..c88a345 100644 --- a/README.md +++ b/README.md @@ -2,47 +2,79 @@ Sort and watermark PDFs exported by GradeScope # Installation -## Virtual environment +Clone this github repository to your machine. -Use a [python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtualenv/) +## Python virtual environment - $ virtualenv ~/Library/virtualenvs/wsm - -(you can call it whatever you want and place it wherever you want.) Activate the virtual environment: +Use the [`pipenv`](https://docs.pipenv.org/#install-pipenv-today) command to manage a [python virtual environment](https://packaging.python.org/guides/installing-using-pip-and-virtualenv/). - $ source ~/Library/virtualenvs/wsm/bin/activate - (wsm):$ which python - /Users/matthew/Library/virtualenvs/wsm/bin/python - -Now anything you do is in the virtual environment, and won't require root access or cluttering your user -python library. +On macOS with MacPorts: + + $ sudo port install pipenv + +On macOS with HomeBrew: + + $ sudo brew install pipenv + +On generic Unix, [install pip](https://pip.pypa.io/en/stable/installing/) if not installed and use: + + $ pip install pipenv + +Then, within this project directory, run: + + $ pipenv install . ## Helper packages -Someday: package the script with an installer that does this automatically. +The `pipenv install` command above should import the packages below into the virtual environment automatically: - $ pip install PyYaml - $ pip install pdf - $ pip install pyPDF - $ pip install Reportlab - $ pip install nameparser + * PyYaml + * pdf + * pyPDF + * ReportLab + * nameparser ## Script -Download the python script. Or, you can clone the full github repository. +The `pipenv install` command above will also install an executable module called `watermarkGradeScopeExport.py`. This is normally what you would run. # Usage +Within the project folder, run: + +$ pipenv shell + +This will activate the virtual environment. You'll get a message such as: + + Spawning environment shell (/bin/bash). Use 'exit' to leave. + bash-3.2$ . /Users/matthew/.local/share/virtualenvs/watermark-pdfs-bOgaLKHv/bin/activate + (watermark-pdfs-bOgaLKHv) bash-3.2$ + +From now on, until you run `exit`, the script `watermarkGradeScopeExport.py` is in scope. + You will need to download submissions from a (presumably graded) Gradescope assignment. 1. Go to the assignment and click "Review Grades" on the left menu column. 2. Look for the "Export Submissions" button in the bottom toolbar. 3. It takes some time, so you'll get a "please wait..." screen. Once it's finished, you'll get a download link on the webpage, and through email. 4. Download the `submissions.zip` file. -5. Unzip it into some working directory. You should see a directory called `assignment_nnnnn_export`, with a bunch of PDFs and a `.yml` file. +5. Unzip it into some working directory. You should see a directory called `assignment_nnnnn_export`, with a bunch of PDFs and a YAML (`.yml`) file. + +Change into that directory and run `watermarkGradeScopeExport.py`. If all goes well, you'll get a directory for each student, with their watermarked PDF therein. + +If you'd rather not use the pipenv shell, you can run the script out of its +vitual environment directory. This requires some extra typing and tabbing: + + $ /Users/matthew/.local/share/virtualenvs/watermark-pdfs-bOgaLKHv/bin/watermarkGradeScopeExport.py + +(Use the directory indicated the first time you run `pipenv shell`) You can +also do fancy things like -Change into that directory and run: + * make a shell alias to that long file path + * make a symbolic link from one of the directories in `$PATH` to the + virtual environment, or + * add the virtual environment's `bin` directory to your `$PATH` + environment variable in your `.login` or `.profile` or similar + file - $ python path/to/script -If all goes well, you'll get a directory for each student, with their watermarked PDF therein. diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..30115b1 --- /dev/null +++ b/setup.py @@ -0,0 +1,8 @@ +from distutils.core import setup + +setup( + name="Watermark GradeScope Export", + version='0.1', + scripts=['watermarkGradeScopeExport.py'] +) + diff --git a/watermarkGradeScopeExport.py b/watermarkGradeScopeExport.py index b07bc3e..46f4629 100644 --- a/watermarkGradeScopeExport.py +++ b/watermarkGradeScopeExport.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + #need to install PyYaml, pyPDF, Reportlab.pdfgen, nameparser from PyPDF2 import PdfFileWriter, PdfFileReader from reportlab.pdfgen import canvas