Metadata-Version: 2.4
Name: jx-loop-hint
Version: 0.0.1
Dynamic: Keywords
Summary: A JupyterLab extension.
Project-URL: Homepage, https://github.com/machine-teaching-group/sigcse2026-closing-the-loop/frontend_student_jx
Project-URL: Bug Tracker, https://github.com/machine-teaching-group/sigcse2026-closing-the-loop/frontend_student_jx/issues
Project-URL: Repository, https://github.com/educational-technology-collective/hintbot.git
Author-email: etc <mphung@mpi-sws.org>
License: BSD 3-Clause License
        
        Copyright (c) 2024, etc
        All rights reserved.
        
        Redistribution and use in source and binary forms, with or without
        modification, are permitted provided that the following conditions are met:
        
        1. Redistributions of source code must retain the above copyright notice, this
           list of conditions and the following disclaimer.
        
        2. Redistributions in binary form must reproduce the above copyright notice,
           this list of conditions and the following disclaimer in the documentation
           and/or other materials provided with the distribution.
        
        3. Neither the name of the copyright holder nor the names of its
           contributors may be used to endorse or promote products derived from
           this software without specific prior written permission.
        
        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
        AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
        DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
        FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
        SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
        CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
        OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
        OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
License-File: LICENSE
Classifier: Framework :: Jupyter
Classifier: Framework :: Jupyter :: JupyterLab
Classifier: Framework :: Jupyter :: JupyterLab :: 4
Classifier: Framework :: Jupyter :: JupyterLab :: Extensions
Classifier: Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.8
Requires-Dist: jupyter-server<3,>=2.0.1
Requires-Dist: jupyterlab-pioneer>=1.6.0
Description-Content-Type: text/markdown

# Student frontend JupyterLab Extension

A JupyterLab extension that serves as an alternative frontend.


## Illustrative screenshots

0. Student programming environment.

<a href="screenshots/student_frontend_jx_environment.png"><img src="screenshots/student_frontend_jx_environment.png" width="800" ></a>

1. Consent the use of the system.

<a href="screenshots/student_frontend_jx_consent.png"><img src="screenshots/student_frontend_jx_consent.png" width="800" ></a>


2. (Optional) View description of the three hint types.

<a href="screenshots/student_frontend_jx_hint_description.png"><img src="screenshots/student_frontend_jx_hint_description.png" width="800" ></a>

3. Click one of the type buttons to request hint. Reflect on the problem.

<a href="screenshots/student_frontend_jx_reflection.png"><img src="screenshots/student_frontend_jx_reflection.png" width="800" ></a>

4. Retrieve AI-generated hints based on the buggy code and reflection

<a href="screenshots/student_frontend_jx_hint.png"><img src="screenshots/student_frontend_jx_hint.png" width="800" ></a>

5. Optional to escalate the request to instructors if the hint is rated as unhelpful.

<a href="screenshots/student_frontend_jx_escalation.png"><img src="screenshots/student_frontend_jx_escalation.png" width="800" ></a>

6. Retrieve instructor feedback if escalated.

<a href="screenshots/student_frontend_jx_feedback.png"><img src="screenshots/student_frontend_jx_feedback.png" width="800" ></a>



## Requirements

- JupyterLab >= 4.0.0


## Development 

Note: You will need NodeJS to build the extension package.


```bash
# Clone the repo to your local environment
# Change directory to the frontend_student_jx directory
# Install package in development mode
source .venv/bin/activate
pip install -e "."
# Link your development version of the extension with JupyterLab
jupyter labextension develop . --overwrite
# Server extension must be manually installed in develop mode
jupyter server extension enable hintbot
# Rebuild extension Typescript source after making changes
jlpm build
```

You can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension.
```bash
# In one terminal:
jlpm run watch          # rebuilds on source changes
# In another terminal:
jupyter lab             # run Jupyterlab
```	

With the watch command running, every saved change will immediately be built locally and available in your running JupyterLab. Refresh JupyterLab to load the change in your browser (you may need to wait several seconds for the extension to be rebuilt).


### Packaging the extension

See [RELEASE](RELEASE.md)

## Publish to PyPI

This project is already configured for Python packaging with Hatch and the JupyterLab builder (prebuilt extension for JupyterLab 4). To rebuild and publish a release on PyPI:


1) Clean everything
`jlpm clean:all`

2) Rebuild the labextension (production)
`jlpm build:prod`


3) Build wheel
`hatch build`

4) Sanity check the build
- Test-install in a clean virtualenv: `pip install dist/*.whl`, start JupyterLab, and confirm the extension is active.

5) Upload to PyPI
- Create a PyPI account and an API token (Settings → API tokens).
- Install `twine`: `pip install twine`.
- Upload: `TWINE_USERNAME=__token__ TWINE_PASSWORD=<pypi-token> twine upload dist/*`.

6) Install as a user
- Users can then install with: `pip install jx-loop-hint`.
- Because this is a prebuilt extension, no `jupyter lab build` step is required for JupyterLab 4.

---

### Notes on Student ID for budget tracking

The extension uses the environment variable `VOC_USERID` to identify the student when making requests to the backend for hint budget tracking. If this environment variable is not set, it falls back to a default, fixed identifier.


### Notes on the student program for a question is extracted from a notebook

The extension extracts a student's program for a particular question by scanning notebook cells starting from the question's markdown cell and concatenating subsequent code cells until it reaches a configured end-marker cell. This extracted program is sent along with other context (e.g., notebook JSON) to the backend when creating an AI hint request.

Key rules implemented by the extension (behavior mirrors `src/requestHint.ts`):

- Question start detection
	- If a `questions.json` configuration is present under `user_customizable_configs/notebook_questions/`, the extension treats any markdown cell whose `nbgrader.grade_id` equals a `question_start_grade_id` in that config as the start of a question.

- Program extraction (end marker)
	- The matching question entry in `questions.json` should provide a `question_end_grade_id`. The extractor will stop concatenating code cells when it encounters a cell whose `nbgrader.grade_id` equals that `question_end_grade_id`.

- What is concatenated
	- Only code cells are appended, in document order, starting from the cell immediately after the question-start markdown cell up to (but not including) the end-marker cell.
	- Cell boundaries are preserved by inserting a single newline between appended cells so the backend can reason about cell separation.

- Robust reading across JupyterLab versions
	- The extractor tries multiple ways to read a cell's text to be resilient across JupyterLab versions and widget/model shapes:
		1. `cell.value.text` (common for ICellModel)
		2. `cell.model.value.text` (widget wrappers)
		3. `sharedModel.getSource()` (some collaborative/shared models)
		4. `cell.input.model.value.text` (widget-level input model)


Example config entry (`user_customizable_configs/notebook_questions/questions.json`):

```json
[
	{
		"question_id": "q1",
		"question_start_grade_id": "cell-abc123",
		"question_end_grade_id": "cell-abc123_assert"
	}
]
```

This will cause the extractor to use `q1` as the `problem_id` sent to the backend and to stop assembling the program when a cell with grade id `cell-abc123_assert` is encountered.

