Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Publish markus-moss package to PyPI

on:
release:
types: [published]

jobs:
run:
name: Publish package
if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
runs-on: ubuntu-latest
environment:
name: pypi
permissions:
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
contents: read
steps:
- uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Install Python
run: uv python install
- name: Build the package
run: uv build
- name: Publish
run: uv publish
31 changes: 31 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Tests

on:
push:
branches: [master]
pull_request:

jobs:
test:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- name: Check out repository
uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v7
with:
python-version: ${{ matrix.python-version }}
enable-cache: true

- name: Sync dependencies
run: uv sync --locked --all-extras --dev

- name: Run tests
run: uv run pytest
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
4 changes: 2 additions & 2 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## [unreleased]

- Add option to only download files from a subset of the groups in an assignment (#1)
## [0.1.0]
- Add option to only download files from a subset of the groups in an assignment (#1)
- Fix bug where values in config file with argparse defaults were not respected (#2)
- Account for a possibly inconsistent structure in moss html (#6)

1 change: 0 additions & 1 deletion MANIFEST.in

This file was deleted.

97 changes: 48 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
## MarkUs Moss
# markus-moss

Tool to generate [Moss](http://moss.stanford.edu/) reports from [MarkUs](https://github.com/MarkUsProject/Markus) submissions.

### Installation
## Installation

```shell script
pip install git+https://github.com/MarkUsProject/markus-moss.git
pip install markus-moss
```

Optional External Dependencies:

- [pandoc](https://pandoc.org/) (required for `copy_files_to_pdf` action)

### Usage
## Usage

```shell script
markusmoss {arguments}
markus-moss {arguments}
```

Running `markusmoss` is guaranteed to be idempotent as long as the `--force` option is not used.
Running `markus-moss` is guaranteed to be idempotent as long as the `--force` option is not used.

#### arguments:
### Arguments

MarkUs moss takes several optional command line arguments. Depending on the action selected, different arguments
are required. If a required argument is not specified, markusmoss.py will raise an error during runtime.
`markus-moss` takes several optional command-line arguments. Depending on the action selected, different arguments
are required. If a required argument is not specified, `markus-moss` will raise an error during runtime.

Arguments can be specified on the command line or in a config file (see below).

* --help : show help message and exit
* --markus-api-key : (string) markus api key
* --markus-url : (string) markus url
* --markus-course : (string) markus course short identifier
* --markus-assignment : (string) markus assignment short identifier
* --moss-userid : (integer) moss userid
* --moss-report-url : (string) moss report url
* --config : (string) config file (see format below) : default is `${PWD}/.markusmossrc`
* --workdir : (string) working directory : default is `${PWD}`
* --actions : (strings) actions (see below) : default is to run all actions in order
* --groups : (strings) only use the groups listed : default is to use all groups from the specified MarkUs assignment
If --groups is not provided but --selected-groups is, then only the selected groups will be used.
* --language : (strings) moss programming languages (see below)
* --file-glob : (string) glob describing submission files to test with moss (see below): default is '\*\*/\*'
* --generate-config: (string) write a config file (see format below) to the path specified from all other arguments given.
If no path is given to this flag, write to stdout.
* --html-parser : (string) bs4 html parser: default is 'html.parser'
* --force : redo all specified actions: default is not to redo previously executed actions
* --verbose : log actions to stdout
* --selected-groups : (int) a single case number or (string) 2 or more group names to generate individual reports for.

#### config format

Instead of passing arguments to the command line, most arguments can be specified in a
[toml](https://github.com/toml-lang/toml) configuration file. All command line arguments can also be specified in the
* `--help`: show help message and exit
* `--markus-api-key`: (string) MarkUs API key
* `--markus-url`: (string) MarkUs URL
* `--markus-course`: (string) MarkUs course short identifier
* `--markus-assignment`: (string) MarkUs assignment short identifier
* `--moss-userid`: (integer) MOSS userid
* `--moss-report-url`: (string) MOSS report URL
* `--config`: (string, default `${PWD}/.markusmossrc`) config file (see format below); default is
* `--workdir`: (string, default `${PWD}`) working directory
* `--actions`: (comma-separated list of strings) actions (see below). Default is to run all actions in order
* `--groups`: (comma-separated list of strings) only use the groups listed.
If `--groups` is not provided but `--selected-groups` is, then only the selected groups will be used.
* `--language`: (comma-separated list of strings) MOSS programming languages (see below)
* `--file-glob`: (string) glob describing submission files to test with MOSS (see below): default is `\*\*/\*`
* `--generate-config`: (string) write a config file (see format below) to the path specified from all other arguments given.
If no path is given to this flag, write to stdout.
* `--html-parser`: (string) bs4 html parser: default is `html.parser`
* `--force`: Redo all specified actions. Default is not to redo previously executed actions.
* `--verbose`: log actions to stdout
* `--selected-groups`: (integer) a single case number or (string) two or more group names to generate individual reports for.

### Config format

Instead of passing arguments to the command line, most arguments can be specified in a
[toml](https://github.com/toml-lang/toml) configuration file. All command line arguments can also be specified in the
config file except:

* config
Expand All @@ -74,7 +74,7 @@ Information about obtaining the `markus_api_key` and `markus_url` can be found h

Information about obtaining the `moss_userid` can be found here: http://moss.stanford.edu/

##### Selecting cases
### Selecting cases
When using the `--selected-groups` argument, the `selected` directory will be generated to produce a folder
with the specified case (in the case of a case number being provided), or all cases involving the
groups (in the case of a list of group names being provided).
Expand All @@ -90,9 +90,9 @@ will be reported.

If `--groups` is not provided, then the groups in `--selected_groups` will be used rather than all groups.

##### Excluding matches
### Excluding matches
Specific matches within cases may also be provided through the `exclude_matches` argument in the `toml`
config file to exclude them from selected results (see above).
config file to exclude them from selected results (see above).

This should map the case number to the match numbers to exclude. For example:

Expand All @@ -101,7 +101,7 @@ exclude_matches = { 1 = [0, 8] }
```
This would exclude matches 0 and 8 from being reported for reports containing case 1.

#### actions
### Actions

* download_submission_files
* Download submission files from MarkUs and write them to the `submission_files` subdirectory
Expand Down Expand Up @@ -156,13 +156,13 @@ This would exclude matches 0 and 8 from being reported for reports containing ca

All subirectories are assumed to be in the specified `workdir`.

#### moss languages
### moss languages

For a full list of programming languages that can be parsed by moss:
For a full list of programming languages that can be parsed by MOSS:

http://moss.stanford.edu/

#### file-glob explanation
### file-glob explanation

MarkUs submissions often contain multiple file types but moss can only evaluate similarity for one language at a time.
In order to specify which files should be sent to moss for evaluation, the file-glob argument can be used.
Expand All @@ -173,7 +173,7 @@ For example, to run moss on python files in any subdirectory from a MarkUs submi
markusmoss --config my_config.toml --language python --file-glob '**/*.py'
```

### Output Files
## Output Files

After running all actions in order, the contents of the `workdir` should look like:

Expand Down Expand Up @@ -216,16 +216,16 @@ workdir/
The all directories except for those contained in `final_report` are not required for the final report and can be
deleted safely once all actions have been run.

#### final report format
### Final Report Format

The final report contains a subdirectory for the MarkUs assignment.
The final report contains a subdirectory for the MarkUs assignment.
Within that directory is a case overview file with the details of each case reported by moss.
There is a subdirectory for each case which contains the submission files for each group in both text and pdf formats.
Each case also contains one group_data.csv file per group which contains information about each group.
Each case also contains a moss.html file which contains the moss report for the specific case in question.
Each case also contains a moss.html file which contains the moss report for the specific case in question.
This report can be viewed by opening the moss.html file in any web browser.

##### group_data.csv format
### group_data.csv Format

This csv file contains information about the group members, it contains the following columns:

Expand All @@ -236,12 +236,11 @@ This csv file contains information about the group members, it contains the foll
- email : the student's email address (if available)
- id_number : the sutdent's id number (if available)

##### case_overview.csv format
### case_overview.csv format

This csv file contains information about the cases reported by moss, it contains the following columns:

- case : the case name
- groups : the group names of all groups compared for the given case (separated by `;`)
- similarity (%) : the percentage similarity score reported by moss
- matched_lines : the number of identical lines of code reported by moss

- matched_lines : the number of identical lines of code reported by moss
55 changes: 55 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
[project]
name = "markus-moss"
version = "0.1.0"
authors = [
{ name="Sophia Huynh" },
{ name="Sam Maldonado" },
{ name="Misha Schwartz" },
]
maintainers = [
{ name="David Liu", email="david@cs.toronto.edu" },
]
description = "Run MOSS plagiarism detector on MarkUs submissions"
readme = "README.md"
requires-python = ">=3.10"
classifiers = [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
]
license = "MIT"
license-files = ["LICEN[CS]E*"]
dependencies = [
"mosspy==1.0.8",
"toml==0.10.2",
"html5lib==1.1",
"pypdf",
"markusapi>=0.5.0",
"requests>=2.32.4",
"urllib3>=2.5.0",
"toc_pdf_merge @ git+https://github.com/sophia-huynh/toc-pdf-merge.git",
]

[project.scripts]
markus-moss = "markus_moss.cli:cli"

[project.urls]
Homepage = "https://github.com/MarkUsProject/markus-moss"
Issues = "https://github.com/MarkUsProject/markus-moss/issues"

[build-system]
requires = ["uv_build >= 0.9.17, <0.10.0"]
build-backend = "uv_build"

[dependency-groups]
dev = ["pytest"]

[tool.uv.workspace]
members = [
"markusapi",
]

[tool.uv.build-backend]
data = [
"src/markus_moss/templates"
]
6 changes: 3 additions & 3 deletions runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ def install_venv(workdir):
python = os.path.join(workdir, 'venv', 'bin', 'python3')
if not os.path.isfile(python):
subprocess.run(['python3', '-m', 'venv', f'{workdir}/venv'], check=True)
if not os.path.isfile(os.path.join(workdir, 'venv', 'bin', 'markusmoss')):
if not os.path.isfile(os.path.join(workdir, 'venv', 'bin', 'markus-moss')):
pip = os.path.join(workdir, 'venv', 'bin', 'pip')
subprocess.run([pip, 'install', 'wheel', 'git+https://github.com/MarkUsProject/markus-moss.git'], check=True)
return os.path.join(workdir, 'venv', 'bin', 'markusmoss')
subprocess.run([pip, 'install', 'wheel', 'markus-moss'], check=True)
return os.path.join(workdir, 'venv', 'bin', 'markus-moss')


def _update_conf_simple(conf, key, description, check=lambda x: x, failure=None):
Expand Down
5 changes: 0 additions & 5 deletions setup.cfg

This file was deleted.

32 changes: 0 additions & 32 deletions setup.py

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading