diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 1537b560..c326d966 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -38,6 +38,7 @@ import ocbpy - [ ] Make sure you are merging into the ``develop`` (not ``main``) branch - [ ] My commits are formatted appropriately (following the SciPy/NumPy style) - [ ] My code follows the style guidelines of this project +- [ ] I assert that I have not used AI in the development of this pull request - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1a07e83d..eaa5e41a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,12 @@ jobs: matrix: os: ["ubuntu-latest", "macos-latest", "windows-latest"] python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] - install-extras: ["base", "pysat_instruments", "dmsp_ssj"] + install-extras: ["base", "dmsp_ssj"] + include: + # Pysat test for only pysat-passing Python version + - python-version: "3.10" + os: "ubuntu-latest" + install-extras: "pysat-instrument" name: Python ${{ matrix.python-version }} on ${{ matrix.os }} with ${{ matrix.install-extras }} runs-on: ${{ matrix.os }} @@ -50,10 +55,9 @@ jobs: coverage report coverage xml --rcfile=pyproject.toml - - name: Coveralls Parallel + - name: Coveralls Parallel uses: coverallsapp/github-action@v2 with: - github-token: ${{ secrets.GITHUB_TOKEN }} flag-name: run=${{ join(matrix.*, '-') }} parallel: true format: cobertura @@ -65,9 +69,7 @@ jobs: runs-on: "ubuntu-latest" steps: - name: Coveralls Finished - env: - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} - COVERALLS_PARALLEL: true - run: | - curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz - ./coveralls done --build-number ${{ github.run_number }} + uses: coverallsapp/github-action@v2 + with: + parallel-finished: true + carryforward: "all" diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5d04921d..7c3caa71 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -88,3 +88,17 @@ To run a subset of tests from the test directory for a specific environment:: To run all the tests for a specific environment:: python -m unittest discover + +Use of 'AI' +----------- + +This project is human centered and developers do not use Artificial Inteligence +(AI) tools such as large language models in its creation or maintenance. +The purpose of this tool is to improve high-latitude science, and thus values +interactions with scientists and scientific programmers. Code generated by a +large language model or similar technology, such as Anthropic’s Claude, +GitHub/Microsoft’s Copilot, OpenAI’s ChatGPT, Facebook/Meta’s Code Llama et al., +is not compliant with the covenants and representations of OCBpy’s Contributor’s +Agreement, and is thus not acceptable as code for OCBpy. + + diff --git a/Changelog.rst b/Changelog.rst index 14351ac6..f63db94a 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -7,7 +7,9 @@ Summary of all changes made since the first stable release ------------------ * ENH: Added the Gussenhoven (1983) model for the EAB * ENH: Added the CH-Aurora-2014 model for the OCB and EAB -* DEP: Removed support for older versions of `zenodo_get` +* ENH: Added an AI usage policy (no AI use allowed) +* DEP: Removed support for older versions of `zenodo_get`, updated download + checks to account for breaking changes made in the newer versions * MAINT: Added support for Python 3.14 * MAINT: Updated GitHub CI actions * MAINT: Updated numpy usage diff --git a/ocbpy/boundaries/dmsp_ssj_files.py b/ocbpy/boundaries/dmsp_ssj_files.py index e556fc2a..52ca5299 100644 --- a/ocbpy/boundaries/dmsp_ssj_files.py +++ b/ocbpy/boundaries/dmsp_ssj_files.py @@ -25,10 +25,9 @@ """ import datetime as dt -from io import StringIO +import hashlib import numpy as np import os -import sys import zipfile import aacgmv2 @@ -108,45 +107,42 @@ def fetch_ssj_boundary_files(stime=None, etime=None, out_dir=None, if not os.path.isdir(out_dir): raise ValueError("can't find the output directory") - # Download the zenodo archive, capturing the output - zenodo_io = StringIO() - sys.stdout = zenodo_io - sys.stderr = zenodo_io - + # Download the zenodo archive zenodo_get.download(doi=doi, output_dir=out_dir) - zenodo_checksum = None - # Parse the output and retrieve files from the zip archive - sys.stdout = sys.__stdout__ - sys.stderr = sys.__stderr__ - zen_msg = zenodo_io.getvalue() - zen_split = zen_msg.split() + # Get the checksum file (does not download) + zenodo_get.download(doi=doi, output_dir=out_dir, md5=True) + zenodo_checksum = os.path.join(out_dir, "md5sums.txt") - if zen_msg.find('Checksum is correct') < 0 and zen_msg.find( - 'already downloaded correctly') < 0: - raise IOError('Bad checksum: {:s}'.format(zen_msg)) + # Verify the checksum and retrieve the zip archive name + if os.path.isfile(zenodo_checksum): + with open(zenodo_checksum, 'r') as zcheck: + csum, zip_name = zcheck.read().split() - # Remove the checksum file if the download problem wasn't found there - if zenodo_checksum is not None: - os.remove(zenodo_checksum) + # Set the archive name + archive_name = os.path.join(out_dir, zip_name) - # Get the archive name from the output - try: - link_ind = zen_split.index('Link:') + 1 + if not os.path.isfile(archive_name): + raise IOError( + 'error downloading archive to output dir: {:}'.format( + archive_name)) - # If the archive is already available, message may differ - zip_name = os.path.split(zen_split[link_ind]) - while zip_name[-1].find(".zip") <= 0: - zip_name = os.path.split(zip_name[0]) + # Get the archive checkshum + with open(os.path.join(out_dir, archive_name), 'rb') as afile: + asum = hashlib.md5(afile.read()).hexdigest() - # Set the archive name - archive_name = os.path.join(out_dir, zip_name[-1]) - except (ValueError, IndexError): - raise IOError('unable to identify zenodo archive: {:}'.format(zen_msg)) + zen_msg = "Checksum is correct" if asum == csum else "Corrupted data" + else: + zen_msg = "Checksum file not created" - if not os.path.isfile(archive_name): - raise IOError('error downloading archive to output dir: {:}'.format( - archive_name)) + # Remove the checksum file + if zenodo_checksum is not None: + os.remove(zenodo_checksum) + + # Evaluate checksum output + if zen_msg.find('Checksum is correct') < 0 and zen_msg.find( + 'already downloaded correctly') < 0: + raise IOError('Bad checksum: {:s}'.format(zen_msg)) # Access the zip archive with zipfile.ZipFile(archive_name, 'r') as zref: