Instructor Notes
================

These are notes about creating and maintaining this repository.  They may be in a state
of flux.

To Do
-----
* Add test for anaconda-package version and issue warning.
* Clean `~/.bash_aliases` with `make realclean`.
* Try to activate the environment only once on CoCalc in Makefile.
* Deal with memory issues (`mamba` everywhere, or `--override-channels` on CoCalc).

## [CoCalc][]

1. Ask Julia to purchase a CoCalc license for the courses you are teaching using the
   educational rate and provisioned for Instructor (which has 2 hour idle timeout for
   use in class).  You should have a `Run Limit` of $N+1$ if you are teaching $N$
   courses (see note below).  E.g. [CoCalc License][].  Have Tom add you as a manager so you
   can apply these to your courses.
   :::{note}
   You need at least 2 here - one for the
   [WSU Courses][] project, and one for the actual shared course project for use in
   class.  If you are teaching multiple classes and students will not be using the
   shared course project at the same time, then you may be able to get by with a `Run
   Limit` of less than $N+1$ (but no less than 2).
   :::
2. Open the [WSU Courses][] project and apply the license above in `Upgrades`.
3. Add any co-instructors or TA's as `Users` for this project.
4. Create the `.course` file.  Note: I keep my courses in the `Archive` folder, and
   symlink the current courses to the top level, for example 
   [`Archive/iSciMath581-ComputeAnything/2025-Phys-581-Fall.course`][].
5. Open the `Shared Project` so you can record the project id (E.g. [Shared CoCalc
   Project - Physics 581 Fall 2025][]).  Add this where needed, e.g. in
   {ref}`sec:Syllabus`.
6. Open the `Configuration` tab and configure the course:
   * **Require Students to Upgrade (Students Pay)**: We find that if students pay for
     this, they are more likely to use it, and the cost is reasonable in lieu of
     textbooks which we try to make available through the library.
     
     :::{note}
     The cost per-student for a term is about $35.  You can lower this cost by doing a
     few things:
     * Reduce the number of days - i.e. set the end date as the date of the last class.
       Make sure that you keep the course long enough for students to complete any exams
       or projects though.
     * Reduce the RAM - the default is 4GB which is suitable for reasonable
       computational tasks, but might be excessive if you only want to gives students a
       taste.
     :::
   * Change the title of the course to something useful like `Phys 581: Compute Anything
     (Fall 2025)` and add a description, e.g.:

     ```markdown
     iSciMath Computing course offered as Physics 581 in the Fall of 2025.
     See the [course notes on ReadTheDocs](
     https://wsu-phys-581-computation.readthedocs.io/fall-2025/) and
     [GitLab source](
     https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation)
     for details.
     ```
     
   * Customize the Email Invitation.
     > Hello!
     > We will use CoCalc for the course *{title}*. This will provide you with an
     > environment where can quickly run and develop code without having to install anything
     > on your computer. 
     > 
     > Please sign up using your WSU email address!
     > 
     > --
     >
     > {name}
   * **Restrict Student Projects**: For a course like this, we ultimately want students
     to be able to do anything, but the interface can be quite complicated, so by
     disabling everything we start simply.  Things can be enabled later.
7. Add yourself as a student so you can see what they will experience.  *(I do this with
   `m.forbes+student@wsu.edu`.)*
8. Install some useful tools like `pipx`, `pixi`, etc.

    ```bash
    wget -qO- https://pixi.sh/install.sh | sh
    ```
    
    ```bash
    python3 -m venv ~/.local/venvs/pipx
    ~/.local/venvs/pipx/bin/python -m pip install pipx
    ln -s ~/.local/venvs/pipx/bin/pipx ~/.local/bin/
    ~/.local/venvs/pipx/bin/pipx ensurepath
    ```
    nad0n2;eeskew;ipjecha;serranolio, sam8ddd, elliot.carmody, bricemwilliams, pchawhan8


## [GitLab][]

1. Create a class repository under <https://gitlab.com/wsu-courses/>.  Make this private.
   * [2025 Physics 581 Class Project][].
   
2. Until proper discussions are supported in GitLab (see [gitlab issue
   #15641](https://gitlab.com/gitlab-org/gitlab/-/issues/15641)), I created an [empty
   project for discussions](https://gitlab.com/wsu-courses/physics-581-2025/discussions)
   inspired by the
   [geo-team/discussions](https://gitlab.com/gitlab-org/geo-team/discussions).
   
[2025 Physics 581 Class Project]: <https://gitlab.com/wsu-courses/2025-physics-581-class-project>

   
[`Archive/iSciMath581-ComputeAnything/2025-Phys-581-Fall.course`]: <https://cocalc.com/projects/c31d20a3-b0af-4bf7-a951-aa93a64395f6/files/Archive/iSciMath581-ComputeAnything/2025-Phys-581-Fall.course>
[WSU Courses]: <https://cocalc.com/projects/c31d20a3-b0af-4bf7-a951-aa93a64395f6>
[CoCalc License]: <https://cocalc.com/store/site-license?user=academic&period=range&range=2025-09-11T07%3A00%3A00.000Z_2026-01-01T07%3A58%3A59.999Z&run_limit=2&cpu=1&ram=8&disk=15&uptime=medium&member=true>


## Other Kernels

We would like to be able to provide other kernels like [xeus-cling][] for C++.  It might
be possible to include these in the main environment, but since they are orthogonal,
they should be installed as separate environments.  [Pixi][] might be able to do this,
but for now, we treat them as "tools", using a custom environment file in `.tools.`.

[xeus-cling]: <https://github.com/jupyter-xeus/xeus-cling>
[Pixi]: <https://pixi.sh/latest/>



## Maintainer Notes
Try to keep this upper-level project as clean as possible, matching the layout expected
for the students.  This will be turned into a skeleton at some point.

## Tools

### Cookie Cutter
* https://github.com/cookiecutter/cookiecutter
* https://cookiecutter-hypermodern-python.readthedocs.io/

### Anaconda Project

```bash
anaconda-project init
mv anaconda-project.yml anaconda-project.yaml  # https://stackoverflow.com/a/22268973/1088938
anaconda-project add-packages python=3.9 scipy matplotlib sphinx notebook ipykernel nb_conda
anaconda-project add-lpackages conda-forge::uncertainties
anaconda-project add-packages conda-forge::sphinx-panels conda-forge::sphinx-book-theme conda-forge::myst-nb
anaconda-project add-packages pytest-cov pytest-flake8 pytest-xdist 
anaconda-project add-packages --pip sphinxcontrib-zopeext sphinxcontrib-bibtex mmf-setup
```

Then I changed all occurrences of `default` to `phys-581` in
`anaconda-project.yaml` so that the environments have a consistent naming convention.

To get everything read after modifying `anaconda-project.yaml`, run:

```bash
anaconda-project prepare --refresh
```

The `--refresh` flag is needed if packages need to be updated, or removed (see [Issue
#214](https://github.com/Anaconda-Platform/anaconda-project/issues/214) for example).

To clean:

```bash
anaconda-project clean
```

[`nbrr`]
: Maybe look at https://github.com/pyoceans/nbrr as a way of generating dependencies.
  This is used by [PyVis examples](https://examples.pyviz.org/make_project.html).

[`nbrr`]: <https://github.com/pyoceans/nbrr> "Jupyter Notebook Reproducible Repositories" 

I add a few commands in `anaconda-project.yaml` to make like easier.  These run after
the environment is activated, which makes them useful:

* `anaconda-project run shell`: Starts a new shell with the current environment active
  (like `poetry shell`.)
* `anaconda-project run init`: Does the full initialization, including setting up the
  kernel.  Typically called by `make init`.
* `anaconda-project run test`, `anaconda-project run test-0`, etc.: Runs `pytest` or the
  corresponding tests for the assignments.  These are used in `.gitlab-ci.yml` to run
  the tests in CI.
* `anaconda-project run <commands>`: Note that this can be used to run any commands with
  the default environment.  For example `anaconda-project run sphinx-autobuild --ignore
  Docs/_build Docs Docs/_build/html`.

## Repository Setup

Can use GitHub, GitLab, or Heptapod.  With automatic pushing to GitHub, one and run the
following CI's:
* LGTM.com

* One course repo.  Students clone their own fork and pull changes.  Assignments
  distributed as tests.
* How to grade?  Student's can keep projects private (but probably will not have access
  to badges.)  Run tests on Student's CoCalc servers or with CI?
  
### Things Students Need to Change
These are things that each student must change in their repo.  These should be fields in
the final skeleton project.

* `pyproject.toml`: `description`, `repository`, `documentation`, and ultimately
  `version`, `license`.
* 

## Best Practices
* Use Jupytext and version control the associated python files.  Only commit the full
  notebooks (with output) when you want to archive documentation.
  * Maybe do this on an "output" branch or something so the main repo does not get
    cluttered?


## Tests and Grading

Tests are setup using [`pytest`].  The configuration is in `pyprojects.toml` and
`.coveragerc`.  Each assignment will have an official test-suite called
`tests/assignmentX/test_official_assignmentX.py` where `X` is the assignment number.
These official tests will be marked with the special marker `assignmentX`, so that the
official tests can be run with:

```bash
pytest -k test_official_assignmentX --no-cov  # Disable coverage.
```

Students should be instructed not to put tests in `tests/assignmentX/test_official_assignmentX.py`

### Reporting

In order to generate badges, we store reporting information in the `_artifacts/` folder
including the following:

* [`_artifacts/htmlcov/index.html`](_artifacts/pytest_report.html): Report of tests.
  (Location specified in `pyproject.toml`.)
* [`_artifacts/junit.xml`](_artifacts/junit.xml): Test reports in junit-format. (Location specified in `pyproject.toml`.)
* [`_artifacts/htmlcov/index.html`](file://_artifacts/htmlcov/index.html): Test coverage
  report. (Location specified in `.coveragerc`.)
* [`_artifacts/coverage.xml`](_artifacts/coverage.xml): Coverage report in cobertura
  format that [GitLab] can use to generate reports as discussed in [GitLab test coverage
  visualization]. (Location specified in `.coveragerc`.)

Note: the "latest artifacts" are [only available if the pipeline
succeeds](https://forum.gitlab.com/t/is-there-a-way-to-ci-latest-artifact-link-work-also-for-failed-jobs/52262/3).
This means that we can't get the badges unless the pipelines pass.  To deal with this,
we make all tests pass artificially by `anaconda-project run test || true` and then use
the badges:

anaconda-project run test 

* ![Tests Badge](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation/-/jobs/artifacts/main/raw/_artifacts/test-badge.svg?job=test)
* ![Coverage Badge](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation/-/jobs/artifacts/main/raw/_artifacts/coverage-badge.svg?job=test)
* ![Assignment 0 Badge](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation/-/jobs/artifacts/main/raw/_artifacts/test-0-badge.svg?job=test-0)
* ![Assignment 1 Badge](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation/-/jobs/artifacts/main/raw/_artifacts/test-1-badge.svg?job=test-1)

## Docs

We use [MyST] for writing the documentation, which is the compiled into HTML, PDF,
etc. with [Sphinx] using the ideas of [JupyerBook] but simply using [Sphinx].  The
documents live in `Docs/` with the exception of `README.md` which lives at the top level
and provides the landing page for the
[GitLab](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation) and
[GitHub](https://github.com/WSU-Physics-Courses/physics-581-physics-inspired-computation)
repos.

To build the documents interactively:

```bash
make doc-server
```

This will run [`sphinx-autobuild`](https://github.com/executablebooks/sphinx-autobuild)
which will launch a webserver on http://127.0.0.1:8000 and rebuild the docs whenever you
save a change.

Here is the play-by-play for setting up the documentation.

```bash
cd Docs
sphinx-quickstart
wget https://brand.wsu.edu/wp-content/themes/brand/images/pages/logos/wsu-signature-vertical.svg -O _static/wsu-logo.svg 
cp -r ../envs/default/lib/python3.9/site-packages/sphinx_book_theme/_templates/* _templates
```

I then edited the `conf.py`.

```bash
hg add local.bib _static/ _templates/
```

**We no longer use substitutions... they are errorprone**.
Substitutions can be used for parameters such as the `{{ class_room }} = `.  These can be defined in `cony.py` in the `myst_substitutions` dictionary and follow
Jinja2 conventions.  I use these to factor common information which will ultimately be
part of the templates.

### [Sphinx]

A couple of notes about using [sphinx] to build to documentation

#### [sphobjinv]

:::{margin}
E.g. the link {py:mod}`sphinx.ext.intersphinx` here is generated with the code
``{py:mod}`sphinx.ext.intersphinx` ``
:::

The  tool is useful for finding {py:mod}`sphinx.ext.intersphinx`
links.  Install it with [pip] in `anaconda-project.yaml`, then use commands like the
following to find the appropriate links:

```bash
sphobjinv suggest -t 90 -u https://www.sphinx-doc.org/objects.inv "intersphinx"
sphobjinv suggest -t 90 -u https://docs.scipy.org/doc/scipy/reference/objects.inv "least_squares"
```

Once you find the appropriate links, you should add the site to your
`conf.py` file along with loading the extension:

```python
# Docs/conf.py
...
extensions = [
  ...
  "sphinx.ext.intersphinx",
  ...
]
...
intersphinx_mapping = {
    "Python 3": ("https://docs.python.org/3", None),
    "matplotlib [stable]": ("https://matplotlib.org/stable/", None),
    "numpy [stable]": ("https://numpy.org/doc/stable/", None),
    "scipy": ("https://docs.scipy.org/doc/scipy/", None),
    "sphinx": ("https://www.sphinx-doc.org/", None),
}
```
  
#### API Documentation

API documentation for the {py:mod}`phys_581` package can be generated using a
combination of {py:mod}`sphinx.ext.autodoc`, {py:mod}`sphinx.ext.autosummary`, and
{py:mod}`sphinx.ext.napolean`.  Here we follow these approaches:

* https://jupyterbook.org/advanced/developers.html
* https://stackoverflow.com/questions/2701998

1. Document your code with [reStructureText] using the [NumPy docstring standard].
2. Prepare your `conf.py` file with at least the following

   ```python
   # Docs/conf.py
   import mmf_setup

   mmf_setup.set_path()  # So that we can import phis_581

   extensions = [
       "sphinx.ext.autodoc",
       "sphinx.ext.autosummary",
       "sphinx.ext.viewcode",
       "sphinx.ext.napoleon",
       "sphinxcontrib.zopeext.autointerface",
   ]
   
   # Make sure that .rst comes first or autosummary will fail.  See
   # https://github.com/sphinx-doc/sphinx/issues/9891
   source_suffix = {  # As of 3.7, dicts are ordered.
       ".rst": "restructuredtext",  # Make sure this is first!
       ".myst": "myst-nb",
       ".md": "myst-nb",
   }
   
   autosummary_generate_overwrite = True

   # Add any paths that contain templates here, relative to this directory.
   templates_path = ['_templates']
   ```
   
3. Update the files `Docs/_templates/custom-class-template.rst` and
   `Docs/_templates/custom-module-template.rst` as [discussed
   here](https://stackoverflow.com/questions/2701998/sphinx-autodoc-is-not-automatic-enough/62613202#62613202)
   
   I added a few extra changes to drop the module from the names so that the TOC is
   cleaner.
   
4. Insert a summary into your `index.md` file or similar.  Here I make an `Docs/api/index.rst`
   file and put the generated docs in `Docs/api/_generated/` (see `:toctree: _generated`
   below).
   
   :::{note}
   You might want instead to remove `_generated` below and set
   `autosummary_generate_overwrite = False` above.  This will generate the files in
   `Docs/api/` but allow you to then modify them.  For real documentation, this is
   probably a better strategy so you can customize things and make them look good.
   :::

   ```rest
   API Reference
   =============

   Documentation of the :py:mod:`phys_581` module.

   <!-- https://github.com/sphinx-doc/sphinx/issues/7552 -->
   #.. autosummary::   <!-- Cant do this or this example gets executed!!! -->
      :toctree: _generated
      :recursive:

      phys_581
   ```

See also:

* See: [Use `sphinx.ext.autodoc` in Markdown
files](https://myst-parser.readthedocs.io/en/latest/sphinx/use.html#use-sphinx-ext-autodoc-in-markdown-files). 
  Unfortunately, one currently still needs to write docstrings in reStructuredText.  See
  [GitHub issue #228](https://github.com/executablebooks/MyST-Parser/issues/228). 
* [Unfortunate issues with autosummary in code blocks.](https://github.com/sphinx-doc/sphinx/issues/7552)

#### Gotchas

* I was getting an error `WARNING: Unexpected section title.` for the `Arguments`
  section containing the parameters for a class constructor.  [Napoleon] should be able
  to parse this, but the issue is how   The has to do with a subtle issue related to how
  the documentation for the constructor is inserted into the class documentation.  (This
  can be controlled by setting the [`autoclass_content`] flag in your Sphinx `conf.py`
  file.)  The solution is to document the constructor parameters in the class
  documentation and use `autoclass_content = "class"` (the default).

[`autoclass_content`]: <https://www.sphinx-doc.org/en/1.0/ext/autodoc.html#confval-autoclass_content>

### Read The Docs

The documents are hosted at [Read the
Docs](https://readthedocs.org/projects/wsu-phys-581-computation/) (RtD)
where they should be build automatically whenever the main branch is pushed.  To get
this working, one needs to tell RtD which packages to install, and they [recommend using
a configuration file](https://docs.readthedocs.io/en/stable/config-file/v2.html) for
this called `.readthedocs.yaml`.

```{warning}
Make sure that the slug is not too long, or you might run into trouble.  The slug is
determined by the project name, so to get this working, when creating the project, I had
to rename the project `wsu-phys-581-computation` to get this as a slug.  After the project
was assigned, I renamed it.  The previous slug was
`physics-581-physics-inspired-computational-techniques` which was so long that I was
getting errors when trying to execute code.
```

### Gotchas

* Be careful not to use [MyST] features in the `README.md` file as this forms the
  landing page on the
  [GitLab](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation) and
  [GitHub](https://github.com/WSU-Physics-Courses/physics-581-physics-inspired-computation)
  repos, neither of which support [MyST].  Check these to be sure that they look okay.
* We literally include the top-level `README.md` files as the first page of the
  documentation in `Docs/index.md`.  This as one side effect that when running `make
  doc-server` (`sphinx-autobuild`), edits to `README.md` do not trigger rebuilding of
  the `index.md` file.  While actively editing, I include `README.md` as a separate
  file, and view that.
* We are using [`anaconda-project`], but [Read the Docs] does not directly support
  provisioning from this, however, you can make the `anaconda-project.yaml` file look
  like an `environment.yaml` file if you [change `packages:` to `dependencies:` as long
  as you can ensure `anaconda-project>=0.8.4`](https://github.com/Anaconda-Platform/anaconda-project/issues/265#issuecomment-903206709).  This allows one to simply install
  this with `conda env --file anaconda-project.yaml`.  
* Since we are just doing a Conda install on RtD, we don't run `anaconda-project run
  init` and the kernel used by our notebooks does not get installed.  We can do this in
  the Sphinx `Docs/conf.py` file.  However, note that this creates a whole new
  environment.  Basically, our previous hack causes RtD to install everything with conda
  somewhere, then when we run `anaconda-project run init` from `Docs/conf.py`, this
  creates **a whole new environment!**.  We customize the kernel installation for RtD in
  `conf.py`. This will have to be kept in sync with `anaconda-project run init`.
* Variables defined in `myst_substitutions` do not seem to be available for use in
  templates.  For this, the definitions must also be added to `html_context`.
* Note that `{% raw %} ... {% endraw %}`  allows you to literally include things like
  MathJaX where the braces confuse Jinja.
  
  ```python
  # Docs/conf.py
  ...
  def setup(app):
      import subprocess

      subprocess.check_call(["anaconda-project", "run", "init"])
  ```
* Don't use `bash` code-blocks when you include output, use `console` instead.  The
  reason is that `bash` code highlighting will fail with `WARNING: Could not lex
  literal_block as "bash". Highlighting skipped.` if your output has an [odd number of
  single quotes](https://github.com/sphinx-doc/sphinx/issues/4098).  Thus write:
  
  ````markdown
  ```console
  $ echo "This line's going to cause a problem"
  This line's going to cause a problem
  ```
  ````
  
  which renders as
  
  ```console
  $ echo "This line's going to cause a problem"
  This line's going to cause a problem
  ```
  
  instead of 
  
  ````
  ```bash
  $ echo "This line's going to cause a problem"
  This line's going to cause a problem
  ```
  ````
  
  which will raise the warning.  *(Interestingly, I could not even nest this block
  without triggering the warning, hence the lack of highlighting in the above block.)*
* Don't forget to include a bibliography somewhere if you are using bibtex.  Otherwise,
  you will get `WARNING: could not find bibtex key ...` warnings.  This should look
  something like:
  
  ````markdown
  ```{bibliography}
  :style: alpha
  ```
  ````
* If you have a notebook that takes more than 30s to execute a cell, you will need to
  increase the execution timeout.  This is done by adding metadata, but not that this
  metadata may be filtered out by [Jupytext].  Start your [MyST] notebooks with least
  the following to ensure you have this:
  
  ```markdown
  ---
  execution:
    timeout: 120
  jupytext:
    formats: ipynb,md:myst
    notebook_metadata_filter: all
  ...
  ---
  ...
  ```

## CoCalc Setup

* [Purchase a license](https://cocalc.com/settings/licenses) with 2 projects to allow
  the course and [WSU Courses CoCalc project] and [Shared CoCalc Project - Physics 581
  Fall 2021] to run.  This approach requires the students to pay $14 for access four the
  term (4 months).  They can optionally use any license they already have instead.
   
  Optionally, one might opt to purchase a license for $n+2$ projects where $n$ is the
  number of students, if there is central funding available.  See [Course Upgrading
  Students](https://doc.cocalc.com/teaching-upgrade-course.html#course-upgrading-students)
  for more details.
  
* Next, [create a course](https://doc.cocalc.com/teaching-create-course.html).  I do
  this in my [WSU Courses CoCalc project] called
  [581-2021](https://cocalc.com/projects/c31d20a3-b0af-4bf7-a951-aa93a64395f6/files/PhysicsInspiredComputation/581-2021.course).
  
  Open this course and create the [Shared CoCalc Project].  Activate the license for
  this project so that it can run.  I then add the SSH key to may `.ssh/config` files so
  I can quickly login.

* Add students, and follow prompt to update the payment option.

* Clone the repos into the shared project and initialize the project.  Optional, but
  highly recommend -- use my [`mmf-setup`] project to provide some useful features

  ```bash
  ssh smc581shared       # My alias in .ssh/config
  python3 -m pip install mmf_setup
  mmf_setup cocalc
  ```
  
  This provides some instructions on how to use the CoCalc configuration.  The most
  important is to forward your user agent and set your `hg` and `git` usernames:
  
  ```bash
  ~$ mmf_setup cocalc
  ...
  If you use version control, then to get the most of the configuration,
  please make sure that you set the following variables on your personal
  computer, and forward them when you ssh to the project:

      # ~/.bashrc or similar
      LC_HG_USERNAME=Your Full Name <your.email.address+hg@gmail.com>
      LC_GIT_USEREMAIL=your.email.address+git@gmail.com
      LC_GIT_USERNAME=Your Full Name

  To forward these, your SSH config file (~/.ssh/config) might look like:

      # ~/.ssh/config
      Host cc-project1
        User ff1cb986f...
    
      Host cc*
        HostName ssh.cocalc.com
        ForwardAgent yes
        SendEnv LC_HG_USERNAME
        SendEnv LC_GIT_USERNAME
        SendEnv LC_GIT_USEREMAIL
        SetEnv LC_EDITOR=vi
  ```
  
  Logout and log back in so we have the forwarded credentials, and now clone the repos.
  
  ```bash
  git clone git@gitlab.com:wsu-courses/physics-581-physics-inspired-computation.git
  cd physics-581-physics-inspired-computation
  make
  ```
  
  The last step runs `git clone
  git@gitlab.com:wsu-courses/physics-581-physics-inspired-computation_resources.git
  _ext/Resources` which puts the resources folder in `_ext/Resources`.

* Create an environment:

  ```bash
  ssh smc581shared
  cd physics-581-physics-inspired-computation
  anaconda2020
  anaconda-project prepare
  conda activate envs/default
  python -m ipykernel install --user --name "phys-581" --display-name "Python 3 (phys-581)"
  ```

  This will create a Conda environment as specified in `anaconda-project.yaml` in `envs/default`.

* For students to be able to use the Linux terminal through their browser, they will
  need to provide appropriate git or mercurial configuration.  They could do this by
  defining environmental variables in their individual project settings:
  
  ```json
  {
    "LC_HG_USERNAME": "Michael McNeil Forbes <michael.forbes+python@gmail.com>",
    "LC_GIT_USERNAME": "Michael McNeil Forbes",
    "LC_GIT_USEREMAIL": "michael.forbes+python@gmail.com",
  }
  ```
  
  If they do not SSH in with a forwarded agent, then they will still need to use their
  password every time they push.  Note: SSH variable forwarding will override these.

### CoCalc Issues

* `/ext/anaconda2020.02/.condarc` includes so many channels that even a simple `conda
  search uncertainties` fails.  `mamba` stills works, so we use this for now.

## GitLab Setup

For [GitLab] access to the private repository, I created a group [Physics
581-2021](https://gitlab.com/phys-581-2021) and invited all the students.  I figure that
it will be easier to update this each year.  I then added this group to the repos.

### CI

The GitLab CI is defined in the file `.gitlab-ci.yml`.  My working strategy is:

1. Use a Docker image for Conda.
2. Update this with `anaconda-project`.
3. Use `anaconda-project run test`.

This works, but takes about [5
minutes](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation/-/pipelines/363108475).
In
principle, it might improve things to cache the virtual environment... Needs testing.

#### Badges

[GitLab] provides some support for generating badges, but it is not very flexible.
Instead, I generate badges as artifacts from the test process.  The following packages
are useful:

* **[`genbadge`]**: Generates badges from `pytest` reports.  Specific for `pytest`,
  `coverage`, and `flake8`, but very convenient for these.
* [`anybadge`]: General badge generation, but you need to feed in the appropriate
  numbers.

To get the badges, go to **Settings > CI/CD > General pipelines***.  I did the following
here:

* Set a small Timeout **20min** (important for students to do so they don't run out on
  private projects).
* Copy the badges.  Note: it is not easy to [change the "pipline"
  name](https://stackoverflow.com/questions/55836220/how-to-change-pipeline-badge-name)
  in the badges.

To get [coverage working](https://docs.gitlab.com/ee/user/project/merge_requests/test_coverage_visualization.html), I needed to:

1. Generate `_htmlcov/coverage.xml` by adding an `[xml]` section to `.coveragerc`.
2. Add the `--cov-report=xml` option in `pyproject.toml` in the
   `[tool.pytest.ini_options]` section.
3. Make the report an `artifact` in `.gitlab-ci.yml`.

With this enabled, I can get the following badges:

[![pipeline status](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation/badges/main/pipeline.svg)](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation/-/commits/main)

[![coverage report](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation/badges/main/coverage.svg)](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation/-/commits/main)

Note: by default it does not seem possible to get different badges for different jobs.
Instead, it seems that one must generate the badges as part of the tests.  I am playing
with the [`anybadge`] project.

* [Customize pipeline configuration (GitLab
  Docs)](https://docs.gitlab.com/ee/ci/pipelines/settings.html)
* [Gitlab - How to add badge based on jobs
  pipeline](https://stackoverflow.com/questions/52228070/gitlab-how-to-add-badge-based-on-jobs-pipeline)
* [GitLab covraged badge with
  pytest](https://www.stddev.tech/gitlab-coverage-badge-with-pytest/): Shows how to
  generate a coverage badge without any artifacts.  We use artifacts anyway so that
  [GitLab] will show in the diffs as discussed in [GitLab test coverage
  visualization].

## Repository Mirrors

It can be useful to mirror your repo to [GitHub] or [GitLab], either to take advantage
of different CI tools, or as a backup.

* [Repository mirroring (from GitLab)](https://docs.gitlab.com/ee/user/project/repository/mirror/).
* [Push mirroring from GitLab or Heptapod to GitLab](https://docs.gitlab.com/ee/user/project/repository/mirror/push.html#set-up-a-push-mirror-to-another-gitlab-instance-with-2fa-activated). 
* [Push mirroring from GitLab or Heptapod to GitHub](https://docs.gitlab.com/ee/user/project/repository/mirror/push.html#set-up-a-push-mirror-from-gitlab-to-github).

### GitHub

[GitHub] has a different set of tools, so it is useful to mirror the repo there so we
can take advantage of these:

* [GitLab Main Repo](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation)
* [GitHub Mirror](https://github.com/WSU-Physics-Courses/physics-581-physics-inspired-computation)

To do this:

1. Create an empty [GitHub
   project](https://github.com/forbes-group/physics-581-physics-inspired-computation).
   Disable
   [Features](https://github.com/WSU-Physics-Courses/physics-581-physics-inspired-computation/settings)
   like `Wikis`, `Issues`, `Projects`, etc. which are not desired for a mirror.
2. Get a personal token from [GitHub] as [described
   here](https://hg.iscimath.org/help/user/project/repository/repository_mirroring#setting-up-a-push-mirror-from-gitlab-to-github).
   Create a token here [**Settings > Developer settings > Personal access
tokens**](https://github.com/settings/tokens) with `repo` access, `admin:repo_hook`
   access, and `delete_repo` access.  Copy the key. 
3. Go to your [**Settings > Repository > Mirroring
   respositories**](https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation/-/settings/repository)
   in you GitLab repo and use the URL to your GitHub repo using the following format:
   `https://<your_github_username>@github.com/<your_github_group>/<your_github_project>.git`.
   I.e.:
   
   ```
   https://mforbes@github.com/WSU-Physics-Courses/physics-581-physics-inspired-computation.git
   ```
   
   Include your name here (the user associated with the key you just generated) and
   use the key as the password.  Choose **Mirror direction** to be **Push**.
   Optionally, you can choose to mirror only protected branches: this would be a good
   choice if you were mirroring a private development repo and only wanted to public
   branches to be available on [GitHub].

Now whenever you push changes to GitLab, they will be mirrored on GitHub, allowing you
to use GitHub features like their CI, Notebook viewer etc.

### GitLab

One can also mirror to [GitLab]. In this case there is little point, but one might like
to mirror from a private instance of [GitLab CE] or [Heptapod] to the [GitLab].  The
procedure is similar.  We will use the [mmfutils] repo. as an example:

* [Heptapod Main Repo](https://alum.mit.edu/www/mforbes/hg/forbes-group/mmfutils)
  *(Permalink that will forward to the [current main repo](https://hg.iscimath.org/forbes-group/mmfutils).)*
* [GitHub Mirror](https://github.com/forbes-group/mmfutils)
* [GitLab Mirror](https://gitlab.com/coldatoms/utilities/mmfutils)

To do this:


1. Create an empty [GitLab project](https://gitlab.com/coldatoms/utilities/mmfutils).
   Disable [project features](https://gitlab.com/coldatoms/utilities/mmfutils/edit)
   like `Wikis`, `Issues`, `Projects`, etc. which are not desired for a mirror.
2. Get a personal token from [GitLab] as [described
   here](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token).
   Create a token here [**Avatar > Edit Profile > Access Tokens > Personal Access
   Tokens**](https://gitlab.com/-/profile/personal_access_tokens) with
   `write_repository` access.  Copy the key. 
3. Go to your [**Settings > Repository > Mirroring
   respositories**](https://hg.iscimath.org/forbes-group/mmfutils/-/settings/repository)
   in you GitLab repo and use the URL to your other GitLab repo using the following format:
   `https://oath2@<destination host>/<your_gitlab_group_or_name>/<your_gitlab_project>.git`.
   
   I.e.:
   
   ```
   https://oath2@gitlab.com/coldatoms/utilities/mmfutils.git
   ```
   
   Choose the appropriate **Mirror type** (`Git push`) and enter the access token you copied
   above as the Password.

Now whenever you push changes to GitLab, they will be mirrored on your other GitLab
repo.



# General Course Preparation

Here are the general steps needed to prepare for the course: see above for further
details.

1. Purchase $N+1$ CoCalc Licenses for $N$ courses for the duration of the course.
2. Create the following as needed (some may be reused from previous terms):

   CoCalc Course:
   : This is the course page where you add students.  It will give each student a
     private project, and create a shared course project.  E.g.:
     * [CoCalc Course File - Physics 581 Fall 2021]
     * [Shared CoCalc Project - Physics 581 Fall 2021]
   
   GitLab Course Project (Public)
   : This is the public repository for the course, with notes, assignments, syllabus,
     etc.  It will likely be reused, or cloned from a previous course.  E.g.:

     * [GitLab Public Project - Physics 581 Fall 2021]

   GitLab Course Resource Project (Private)
   : This is a private repository with things that should only be shared with the class,
     such as textbooks, data, etc.  *(Currently this is doing double duty as it is also
     used for data that could in principle be shared publicaly.  We may need to add a
     third repo later.)* E.g.:

     * [GitLab Resources Project - Physics 581 Fall 2021]
     
   GitLab Group for the course
   : To give students access to the course, I add them to a [GitLab] group, then I give
     the group access to the various projects above.  This makes it easy to switch
     classes, and to add students.  E.g.:

     * [Physics 581-2021 GitLab Group](https://gitlab.com/phys-581-2021)
     
   GitHub Mirror (Public)
   : This is a public mirror of the GitLab course project on [GitHub], allowing us to
     use their CI tools.  E.g.:
   
     * [GitHub Mirror - Physics 581 Fall 2021]


<!-- Links -->
[sphinx]: <https://www.sphinx-doc.org/>
[sphinx.ext.autodoc]: <https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html> "Sphinx autodoc extension"
[sphinx.ext.autosummary]: <https://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html>
[CoCalc]: <https://cocalc.com> "CoCalc: Collaborative Calculation and Data Science"
[Conda]: <https://docs.conda.io/en/latest/> "Conda: Package, dependency and environment management for any language—Python, R, Ruby, Lua, Scala, Java, JavaScript, C/ C++, FORTRAN, and more."
[Fil]: <https://pythonspeed.com/products/filmemoryprofiler/> "The Fil memory profiler for Python"
[GitHub CI]: <https://docs.github.com/en/actions/guides/about-continuous-integration> "GitHub CI"
[GitHub]: <https://github.com> "GitHub"
[GitLab]: <https://gitlab.com> "GitLab"
[GitLab CE]: <https://about.gitlab.com/install/?version=ce> "GitLab Community Edition"
[Git]: <https://git-scm.com> "Git"
[Heptapod]: <https://heptapod.net> "Heptapod: is a community driven effort to bring Mercurial SCM support to GitLab"
[Hypermodern Python]: <https://cjolowicz.github.io/posts/hypermodern-python-01-setup/> "Hypermodern Python"
[Jupyter]: <https://jupyter.org> "Jupyter"
[Jupytext]: <https://jupytext.readthedocs.io> "Jupyter Notebooks as Markdown Documents, Julia, Python or R Scripts"
[LGTM]: <https://lgtm.com/> "Continuous security analysis: A code analysis platform for finding zero-days and preventing critical vulnerabilities"
[Mercurial]: <https://www.mercurial-scm.org> "Mercurial"
[Miniconda]: <https://docs.conda.io/en/latest/miniconda.html> "Miniconda is a free minimal installer for conda."
[MyPI]: <https://alum.mit.edu/www/mforbes/mypi/> "MyPI: My personal package index"
[MyST]: <https://myst-parser.readthedocs.io/en/latest/> "MyST - Markedly Structured Text"
[Nox]: <https://nox.thea.codes> "Nox: Flexible test automation"
[Poetry]: <https://poetry.eustace.io> "Python packaging and dependency management made easy."
[PyPI]: <https://pypi.org> "PyPI: The Python Package Index"
[Read the Docs]: <https://readthedocs.org> "Read the Docs homepage"
[WSU Physics]: <https://physics.wsu.edu> "WSU Physics Department"
[`anaconda-project`]: <https://anaconda-project.readthedocs.io> "Anaconda Project: Tool for encapsulating, running, and reproducing data science projects."
[`anybadge`]: <https://github.com/jongracecox/anybadge> "Python project for generating badges for your projects"
[`conda-forge`]: <https://conda-forge.org/> "A community-led collection of recipes, build infrastructure and distributions for the conda package manager."
[`conda-pack`]: <https://conda.github.io/conda-pack/> "Command line tool for creating archives of conda environments"
[`genbadge`]: <https://smarie.github.io/python-genbadge/> "Generate badges for tools that do not provide one."
[`minconda`]: <https://docs.conda.io/en/latest/miniconda.html> "Miniconda"
[`mmf-setup`]: <https://pypi.org/project/mmf-setup/> "PyPI mmf-setup page"
[`pyenv`]: <https://github.com/pyenv/pyenv> "Simple Python Version Management: pyenv"
[`pytest`]: <https://docs.pytest.org> "pytest: helps you write better programs"
[hg-git]: <https://hg-git.github.io> "The Hg-Git mercurial plugin"
[pytest]: <https://docs.pytest.org> "pytest"
[venv]: <https://docs.python.org/3/library/venv.html> "Creation of virtual environments"
[pip]: <https://pip.pypa.io/en/stable/> "the package installer for Python"
[sphobjinv]: <https://github.com/bskinn/sphobjinv> "Manipulate and inspect Sphinx objects.inv files"
[NumPy docstring standard]: <https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard>
[WSU Courses CoCalc project]: <https://cocalc.com/projects/c31d20a3-b0af-4bf7-a951-aa93a64395f6>

<!-- Fall 2021 links -->
[CoCalc Course File - Physics 581 Fall 2021]: <https://cocalc.com/projects/c31d20a3-b0af-4bf7-a951-aa93a64395f6/files/PhysicsInspiredComputation/581-2021.course>
[GitHub Mirror - Physics 581 Fall 2021]: <https://github.com/WSU-Physics-Courses/physics-581-physics-inspired-computation> "GitHub mirror"
[GitLab Public Project - Physics 581 Fall 2021]: <https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation> "GitLab public course project for Fall 2021."
[GitLab Resources Project - Physics 581 Fall 2021]: <https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation_resources> "GitLab private resources course project for Fall 2021."
[GitLab test coverage visualization]: <https://docs.gitlab.com/ee/user/project/merge_requests/test_coverage_visualization.html>
[Official Course Repository]: <https://gitlab.com/wsu-courses/2025-physics-581-class-project> "Official Physics 581 Repository hosted on GitLab for 2025"
[Resources project]: <https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation_resources> "Private course resources repository."
[Shared CoCalc Project - Physics 581 Fall 2021]: <https://cocalc.com/projects/74852aba-2484-4210-9cf0-e7902e5838f4/> "581-2021 Shared CoCalc Project"
[Shared CoCalc Project - Physics 581 Fall 2025]: <https://cocalc.com/projects/3f01fb1c-ecf4-462a-9b2d-cee975d9fffc/> "581-2025 Shared CoCalc Project"
[Shared CoCalc Project]: <https://cocalc.com/projects/3f01fb1c-ecf4-462a-9b2d-cee975d9fffc/> "581-2021 Shared CoCalc Project"
[file an issue]: <https://gitlab.com/wsu-courses/physics-581-physics-inspired-computation/-/issues> "Issues on the class GitLab project."
[napoleon]: <https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html>
[Shared CoCalc Project - Physics 581 Fall 2025]: <https://cocalc.com/projects/3f01fb1c-ecf4-462a-9b2d-cee975d9fffc>
<!-- End Links -->

