diff --git a/.github/workflows/collect-user-submission.py b/.github/workflows/collect-user-submission.py deleted file mode 100644 index a70bb32fd..000000000 --- a/.github/workflows/collect-user-submission.py +++ /dev/null @@ -1,82 +0,0 @@ -import json -import os -import typing - -import frontmatter -import pydantic -from markdown_it import MarkdownIt - - -class Author(pydantic.BaseModel): - name: str = 'anonymous' - affiliation: str = None - affiliation_url: typing.Union[str, pydantic.HttpUrl] = None - email: typing.Union[str, pydantic.EmailStr] = None - - -class Submission(pydantic.BaseModel): - title: str - description: str - url: pydantic.HttpUrl - thumbnail: typing.Union[str, pydantic.HttpUrl] = None - authors: typing.List[Author] = None - tags: typing.Dict[str, typing.List[str]] = None - - -@pydantic.dataclasses.dataclass -class IssueInfo: - gh_event_path: pydantic.FilePath - submission: Submission = pydantic.Field(default=None) - - def create_submission(self): - self._get_inputs() - self._create_submission_input() - return self - - def _get_inputs(self): - with open(self.gh_event_path) as f: - self.data = json.load(f) - - self.author = self.data['issue']['user']['login'] - self.title = self.data['issue']['title'] - self.body = self.data['issue']['body'] - - def _create_submission_input(self): - md = MarkdownIt() - inputs = None - for token in md.parse(self.body): - if token.tag == 'code': - inputs = frontmatter.loads(token.content).metadata - break - name = inputs.get('name') - title = inputs.get('title') - description = inputs.get('description') - url = inputs.get('url') - thumbnail = inputs.get('thumbnail') - _authors = inputs.get('authors') - authors = [] - if _authors: - for item in _authors: - authors.append( - Author( - name=item.get('name', 'anyonymous'), - affiliation=item.get('affiliation'), - affiliation_url=item.get('affiliation_url'), - email=item.get('email', ''), - ) - ) - else: - authors = [Author(name='anyonymous')] - _tags = inputs.get( - 'tags', {'packages': ['unspecified'], 'formats': ['unspecified'], 'domains': ['unspecified']} - ) - self.submission = Submission( - name=name, title=title, description=description, url=url, thumbnail=thumbnail, authors=authors, tags=_tags - ) - - -if __name__ == '__main__': - issue = IssueInfo(gh_event_path=os.environ['GITHUB_EVENT_PATH']).create_submission() - inputs = issue.submission.model_dump_json() - with open('resource-gallery-submission-input.json', 'w') as f: - json.dump(inputs, f) diff --git a/.github/workflows/get-metrics.py b/.github/workflows/get-metrics.py deleted file mode 100644 index ca72afe70..000000000 --- a/.github/workflows/get-metrics.py +++ /dev/null @@ -1,309 +0,0 @@ -import datetime -import json -import os - -import cartopy -import google -import matplotlib -import matplotlib.cm as cm -import matplotlib.colors as colors -import matplotlib.pyplot as plt -import numpy as np -from google.analytics.data_v1beta import BetaAnalyticsDataClient -from google.analytics.data_v1beta.types import DateRange, Dimension, Metric, RunReportRequest - -# Project ID Numbers -PORTAL_ID = '266784902' -FOUNDATIONS_ID = '281776420' -COOKBOOKS_ID = '324070631' - -# Access Secrets -PRIVATE_KEY_ID = os.environ.get('PRIVATE_KEY_ID') -# Ensure GH secrets doesn't intrudce extra '\' new line characters (related to '\' being an escape character) -PRIVATE_KEY = os.environ.get('PRIVATE_KEY').replace('\\n', '\n') - -credentials_dict = { - 'type': 'service_account', - 'project_id': 'cisl-vast-pythia', - 'private_key_id': PRIVATE_KEY_ID, - 'private_key': PRIVATE_KEY, - 'client_email': 'pythia-metrics-api@cisl-vast-pythia.iam.gserviceaccount.com', - 'client_id': '113402578114110723940', - 'auth_uri': 'https://accounts.google.com/o/oauth2/auth', - 'token_uri': 'https://oauth2.googleapis.com/token', - 'auth_provider_x509_cert_url': 'https://www.googleapis.com/oauth2/v1/certs', - 'client_x509_cert_url': 'https://www.googleapis.com/robot/v1/metadata/x509/pythia-metrics-api%40cisl-vast-pythia.iam.gserviceaccount.com', - 'universe_domain': 'googleapis.com', -} - -try: - client = BetaAnalyticsDataClient.from_service_account_info(credentials_dict) -except google.auth.exceptions.MalformedError as e: - print('Malformed Error:', repr(e)) - # Insight into reason for failure without exposing secret key - # 0: Secret not found, else malformed - # 706: extra quote, 732: extra '\', 734: both - print('Length of PRIVATE_KEY:', len(PRIVATE_KEY)) - -pre_project_date = '2020-03-31' # Random date before project start - - -def _format_rounding(value): - """ - Helper function for rounding string displays. 1,232 -> 1.2K - """ - return f'{round(value / 1000, 1):.1f}K' - - -# The rest of this file alternates between functions for requesting information from Google Analytics -# And functions that use that request image to form either a .json or a .png file to be used in write-metrics-md.py -def _run_total_users_report(property_id): - """ - Function for requesting cumulative active users from a project since project start. - """ - request = RunReportRequest( - property=f'properties/{property_id}', - dimensions=[], - metrics=[Metric(name='activeUsers')], - date_ranges=[DateRange(start_date=pre_project_date, end_date='today')], - ) - response = client.run_report(request) - - total_users = 0 - for row in response.rows: - total_users += int(row.metric_values[0].value) - - return _format_rounding(total_users) - - -def get_total_users(PORTAL_ID, FOUNDATIONS_ID, COOKBOOKS_ID): - """ - Function for taking cumulative active users from each project and dumping it into a JSON with the current datetime. - """ - metrics_dict = {} - metrics_dict['Now'] = str(datetime.datetime.now()) - metrics_dict['Portal'] = _run_total_users_report(PORTAL_ID) - metrics_dict['Foundations'] = _run_total_users_report(FOUNDATIONS_ID) - metrics_dict['Cookbooks'] = _run_total_users_report(COOKBOOKS_ID) - - # Save to JSON, Remember Action is called from root directory - with open('portal/metrics/user_metrics.json', 'w') as outfile: - json.dump(metrics_dict, outfile) - - -def _run_active_users_this_year(property_id): - """ - Function for requesting active users by day from a project since year start. - """ - current_year = datetime.datetime.now().year - start_date = f'{current_year}-01-01' - - request = RunReportRequest( - property=f'properties/{property_id}', - dimensions=[Dimension(name='date')], - metrics=[Metric(name='activeUsers')], - date_ranges=[DateRange(start_date=start_date, end_date='today')], - ) - response = client.run_report(request) - - dates = [] - user_counts = [] - for row in response.rows: - date_str = row.dimension_values[0].value - date = datetime.datetime.strptime(date_str, '%Y%m%d') - dates.append(date) - user_counts.append(int(row.metric_values[0].value)) - - # Days need to be sorted chronologically - return zip(*sorted(zip(dates, user_counts), key=lambda x: x[0])) - - -def plot_projects_this_year(PORTAL_ID, FOUNDATIONS_ID, COOKBOOKS_ID): - """ - Function for taking year-to-date active users by day and plotting it for each project. - """ - portal_dates, portal_users = _run_active_users_this_year(PORTAL_ID) - foundations_dates, foundations_users = _run_active_users_this_year(FOUNDATIONS_ID) - cookbooks_dates, cookbooks_users = _run_active_users_this_year(COOKBOOKS_ID) - - # Plotting code - plt.figure(figsize=(10, 5.5)) - plt.title('Year-to-Date Pythia Active Users', fontsize=15) - - plt.plot(portal_dates, portal_users, color='purple', label='Portal') - plt.plot(foundations_dates, foundations_users, color='royalblue', label='Foundations') - plt.plot(cookbooks_dates, cookbooks_users, color='indianred', label='Cookbooks') - - plt.legend(fontsize=12, loc='upper right') - - plt.xlabel('Date', fontsize=12) - plt.savefig('portal/metrics/thisyear.png', bbox_inches='tight') - - -def _run_top_pages_report(property_id): - """ - Function for requesting top 5 pages from a project. - """ - request = RunReportRequest( - property=f'properties/{property_id}', - dimensions=[Dimension(name='pageTitle')], - date_ranges=[DateRange(start_date=pre_project_date, end_date='today')], - metrics=[Metric(name='screenPageViews')], - ) - response = client.run_report(request) - - views_dict = {} - for row in response.rows: - page = row.dimension_values[0].value - views = int(row.metric_values[0].value) - views_dict[page] = views - - # Sort by views and grab the top 5 - top_pages = sorted(views_dict.items(), key=lambda item: item[1], reverse=True)[:5] - # String manipulation on page titles "Cartopy - Pythia Foundations" -> "Cartopy" - pages = [page.split('—')[0] for page, _ in top_pages] - views = [views for _, views in top_pages] - - # Reverse order of lists, so they'll plot with most visited page on top (i.e. last) - return pages[::-1], views[::-1] - - -def plot_top_pages(PORTAL_ID, FOUNDATIONS_ID, COOKBOOKS_ID): - """ - Function that takes the top 5 viewed pages for all 3 projects and plot them on a histogram. - """ - portal_pages, portal_views = _run_top_pages_report(PORTAL_ID) - foundations_pages, foundations_views = _run_top_pages_report(FOUNDATIONS_ID) - cookbooks_pages, cookbooks_views = _run_top_pages_report(COOKBOOKS_ID) - - # Plotting code - fig, ax = plt.subplots(figsize=(10, 5.5)) - plt.title('All-Time Top Pages', fontsize=15) - - y = np.arange(5) # 0-4 for Cookbooks - y2 = np.arange(6, 11) # 6-10 for Foundations - y3 = np.arange(12, 17) # 12-16 for Portal - - bar1 = ax.barh(y3, portal_views, align='center', label='Portal', color='purple') - bar2 = ax.barh(y2, foundations_views, align='center', label='Foundations', color='royalblue') - bar3 = ax.barh(y, cookbooks_views, align='center', label='Cookbooks', color='indianred') - - y4 = np.append(y, y2) - y4 = np.append(y4, y3) # 0-4,6-19,12-6 for page labels to have a gap between projects - pages = cookbooks_pages + foundations_pages + portal_pages # List of all pages - ax.set_yticks(y4, labels=pages, fontsize=12) - - # Adds round-formatted views label to end of each bar - ax.bar_label(bar1, fmt=_format_rounding, padding=5, fontsize=10) - ax.bar_label(bar2, fmt=_format_rounding, padding=5, fontsize=10) - ax.bar_label(bar3, fmt=_format_rounding, padding=5, fontsize=10) - - ax.set_xscale('log') - ax.set_xlim([10, 10**5]) # set_xlim must be after setting xscale to log - ax.set_xlabel('Page Views', fontsize=12) - - plt.legend(fontsize=12, loc='lower right') - plt.savefig('portal/metrics/toppages.png', bbox_inches='tight') - - -def _run_usersXcountry_report(property_id): - """ - Function for requesting users by country for a project. - """ - request = RunReportRequest( - property=f'properties/{property_id}', - dimensions=[Dimension(name='country')], - metrics=[Metric(name='activeUsers')], - date_ranges=[DateRange(start_date=pre_project_date, end_date='today')], - ) - response = client.run_report(request) - - user_by_country = {} - for row in response.rows: - country = row.dimension_values[0].value - users = int(row.metric_values[0].value) - user_by_country[country] = user_by_country.get(country, 0) + users - - return user_by_country - - -def plot_usersXcountry(FOUNDATIONS_ID): - """ - Function for taking users by country for Pythia Foundations and plotting them on a map. - """ - users_by_country = _run_usersXcountry_report(FOUNDATIONS_ID) - - # Google API Country names do not match Cartopy Country Shapefile names - dict_api2cartopy = { - 'Tanzania': 'United Republic of Tanzania', - 'United States': 'United States of America', - 'Congo - Kinshasa': 'Democratic Republic of the Congo', - 'Bahamas': 'The Bahamas', - 'Timor-Leste': 'East Timor', - 'C\u00f4te d\u2019Ivoire': 'Ivory Coast', - 'Bosnia & Herzegovina': 'Bosnia and Herzegovina', - 'Serbia': 'Republic of Serbia', - 'Trinidad & Tobago': 'Trinidad and Tobago', - } - - for key in dict_api2cartopy: - users_by_country[dict_api2cartopy[key]] = users_by_country.pop(key) - - # Sort by views and grab the top 10 countries for a text box - top_10_countries = sorted(users_by_country.items(), key=lambda item: item[1], reverse=True)[:10] - top_10_text = '\n'.join( - f'{country}: {_format_rounding(value)}' for i, (country, value) in enumerate(top_10_countries) - ) - - # Plotting code - fig = plt.figure(figsize=(10, 4)) - ax = plt.axes(projection=cartopy.crs.PlateCarree(), frameon=False) - ax.set_title('All-Time Pythia Foundations Users by Country', fontsize=15) - - shapefile = cartopy.io.shapereader.natural_earth(category='cultural', resolution='110m', name='admin_0_countries') - reader = cartopy.io.shapereader.Reader(shapefile) - countries = reader.records() - - colormap = plt.get_cmap('Blues') - newcmp = colors.ListedColormap(colormap(np.linspace(0.2, 1, 128))) # Truncate colormap to remove white hues - newcmp.set_extremes(under='grey') - - norm = colors.LogNorm(vmin=1, vmax=max(users_by_country.values())) # Plot on log scale - mappable = cm.ScalarMappable(norm=norm, cmap=newcmp) - - # Loop through countries and plot their color - for country in countries: - country_name = country.attributes['SOVEREIGNT'] - if country_name in users_by_country.keys(): - facecolor = newcmp(norm(users_by_country[country_name])) - ax.add_geometries( - [country.geometry], - cartopy.crs.PlateCarree(), - facecolor=facecolor, - edgecolor='white', - linewidth=0.7, - norm=matplotlib.colors.LogNorm(), - ) - else: - ax.add_geometries( - [country.geometry], cartopy.crs.PlateCarree(), facecolor='grey', edgecolor='white', linewidth=0.7 - ) - - # Add colorbar - cax = fig.add_axes([0.05, -0.015, 0.7, 0.03]) # [x0, y0, width, height] - cbar = fig.colorbar(mappable=mappable, cax=cax, spacing='uniform', orientation='horizontal', extend='min') - cbar.set_label('Unique Users') - - # Add top 10 countries text - props = dict(boxstyle='round', facecolor='white', edgecolor='white') - ax.text(1.01, 0.5, top_10_text, transform=ax.transAxes, fontsize=12, verticalalignment='center', bbox=props) - - plt.tight_layout() - plt.savefig('portal/metrics/bycountry.png', bbox_inches='tight') - - -if __name__ == '__main__': - get_total_users(PORTAL_ID, FOUNDATIONS_ID, COOKBOOKS_ID) - plot_projects_this_year(PORTAL_ID, FOUNDATIONS_ID, COOKBOOKS_ID) - plot_top_pages(PORTAL_ID, FOUNDATIONS_ID, COOKBOOKS_ID) - plot_usersXcountry(FOUNDATIONS_ID) diff --git a/.github/workflows/nightly-build.yaml b/.github/workflows/nightly-build.yaml index f2ab77535..74b57f515 100644 --- a/.github/workflows/nightly-build.yaml +++ b/.github/workflows/nightly-build.yaml @@ -3,20 +3,27 @@ name: nightly-build on: workflow_dispatch: schedule: - - cron: '0 0 * * *' # Daily “At 00:00” + - cron: "0 0 * * *" # Daily “At 00:00” jobs: build: if: ${{ github.repository_owner == 'ProjectPythia' }} uses: ProjectPythia/cookbook-actions/.github/workflows/build-book.yaml@main with: - environment_file: 'environment.yml' - environment_name: pythia + base_url: '' path_to_notebooks: 'portal' - build_command: 'make -j4 html' + secrets: + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + PRIVATE_KEY_ID: ${{ secrets.PRIVATE_KEY_ID }} + + deploy: + needs: build + uses: ProjectPythia/cookbook-actions/.github/workflows/deploy-book.yaml@main + with: + publish_dir: 'portal/_build/html' link-check: if: ${{ github.repository_owner == 'ProjectPythia' }} - uses: ./.github/workflows/sphinx-link-checker.yaml + uses: ProjectPythia/cookbook-actions/.github/workflows/link-checker.yaml@main with: - path_to_source: 'portal' + path_to_notebooks: 'portal' diff --git a/.github/workflows/publish-site.yaml b/.github/workflows/publish-site.yaml index adcc7e9bb..4953bad67 100644 --- a/.github/workflows/publish-site.yaml +++ b/.github/workflows/publish-site.yaml @@ -6,44 +6,17 @@ on: branches: - main workflow_dispatch: - schedule: - - cron: '0 0 * * 1' # Weekly on Monday jobs: - automate-metrics: - runs-on: macos-latest - steps: - - uses: actions/checkout@v3 - - name: Automate Metrics - env: - PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} - PRIVATE_KEY_ID: ${{ secrets.PRIVATE_KEY_ID }} - run: | - python -m venv analytics-api - source analytics-api/bin/activate - pip install google-analytics-data cartopy matplotlib - - python .github/workflows/get-metrics.py - python .github/workflows/write-metrics-md.py - - name: Upload zip - uses: actions/upload-artifact@v4 - with: - name: repo-zip - path: . - include-hidden-files: 'true' - build: - needs: automate-metrics uses: ProjectPythia/cookbook-actions/.github/workflows/build-book.yaml@main with: - environment_file: 'environment.yml' - environment_name: pythia + base_url: '' path_to_notebooks: 'portal' - build_command: 'make -j4 html' - build_from_code_artifact: 'true' - code_artifact_name: 'repo-zip' - workflow: '' - workflow_conclusion: '' + secrets: + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + PRIVATE_KEY_ID: ${{ secrets.PRIVATE_KEY_ID }} + deploy: needs: build uses: ProjectPythia/cookbook-actions/.github/workflows/deploy-book.yaml@main diff --git a/.github/workflows/sphinx-link-checker.yaml b/.github/workflows/sphinx-link-checker.yaml deleted file mode 100644 index 96ce43268..000000000 --- a/.github/workflows/sphinx-link-checker.yaml +++ /dev/null @@ -1,67 +0,0 @@ -name: sphinx-link-checker - -on: - workflow_call: - inputs: - environment_name: - description: 'Name of conda environment to activate' - required: false - default: 'pythia' - type: string - environment_file: - description: 'Name of conda environment file' - required: false - default: 'environment.yml' - type: string - path_to_source: - description: 'Location of the sphinx source relative to repo root' - required: false - default: './' - type: string - use_cached_environment: - description: 'Flag for whether we should attempt to retrieve a previously cached environment.' - required: false - default: 'true' - type: string # had a lot of trouble with boolean types, see https://github.com/actions/runner/issues/1483 - -jobs: - link-checker: - runs-on: ubuntu-latest - defaults: - run: - shell: bash -l {0} - steps: - - uses: actions/checkout@v3 - - - name: Setup Miniforge - uses: conda-incubator/setup-miniconda@v2 - with: - miniforge-version: latest - python-version: "3.10" # binderbot is failing with python 3.11 - activate-environment: ${{ inputs.environment_name }} - - - name: Set cache date - if: inputs.use_cached_environment == 'true' - run: echo "DATE=$(date +'%Y%m%d')" >> $GITHUB_ENV - - - uses: actions/cache@v3 - if: inputs.use_cached_environment == 'true' - with: - path: /usr/share/miniconda3/envs/${{ inputs.environment_name }} - # This should create a key that looks like 'linux-64-conda-environment.yml-[HASH]-DATE' - # Logic inspired by https://dev.to/epassaro/caching-anaconda-environments-on-github-actions-2d08 - key: ${{ format('linux-64-conda-{0}-{1}-{2}', inputs.environment_file, hashFiles(format('{0}', inputs.environment_file)), env.DATE )}} - id: cache - - - name: Update execution environment - if: | - (inputs.use_cached_environment != 'true' - || steps.cache.outputs.cache-hit != 'true') - run: | - conda env update -n ${{ inputs.environment_name }} -f ${{ inputs.environment_file }} - conda install -c conda-forge sphinxcontrib-applehelp=1.0.4 sphinxcontrib-devhelp=1.0.2 sphinxcontrib-htmlhelp=2.0.1 sphinxcontrib-qthelp=1.0.3 sphinxcontrib-serializinghtml=1.1.5 - - - name: Check external links - run: | - cd ${{ inputs.path_to_source }} - make linkcheck diff --git a/.github/workflows/trigger-link-check.yaml b/.github/workflows/trigger-link-check.yaml index d2ceb8c6b..d9db119e7 100644 --- a/.github/workflows/trigger-link-check.yaml +++ b/.github/workflows/trigger-link-check.yaml @@ -4,6 +4,6 @@ on: jobs: link-check: - uses: ./.github/workflows/sphinx-link-checker.yaml + uses: ProjectPythia/cookbook-actions/.github/workflows/link-checker.yaml@main with: - path_to_source: 'portal' + path_to_notebooks: 'portal' diff --git a/.github/workflows/trigger-preview.yaml b/.github/workflows/trigger-preview.yaml index c3aea861f..e2974a45e 100644 --- a/.github/workflows/trigger-preview.yaml +++ b/.github/workflows/trigger-preview.yaml @@ -10,16 +10,15 @@ on: jobs: find-pull-request: uses: ProjectPythia/cookbook-actions/.github/workflows/find-pull-request.yaml@main - deploy-preview: needs: find-pull-request if: github.event.workflow_run.conclusion == 'success' uses: ProjectPythia/cookbook-actions/.github/workflows/deploy-book.yaml@main with: artifact_name: book-zip-${{ needs.find-pull-request.outputs.number }} - destination_dir: _preview/${{ needs.find-pull-request.outputs.number }} # deploy to subdirectory labeled with PR number - is_preview: 'true' - publish_dir: 'portal/_build/html' + destination_dir: _preview/${{ needs.find-pull-request.outputs.number }} # deploy to subdirectory labeled with PR number + is_preview: "true" + publish_dir: "portal/_build/html" preview-comment: needs: find-pull-request diff --git a/.github/workflows/trigger-site-build.yaml b/.github/workflows/trigger-site-build.yaml index ed8488cd9..b536768c5 100644 --- a/.github/workflows/trigger-site-build.yaml +++ b/.github/workflows/trigger-site-build.yaml @@ -6,9 +6,7 @@ jobs: build: uses: ProjectPythia/cookbook-actions/.github/workflows/build-book.yaml@main with: - environment_file: 'environment.yml' - environment_name: pythia artifact_name: book-zip-${{ github.event.number }} + base_url: '/_preview/${{ github.event.number }}' path_to_notebooks: 'portal' - build_command: 'make -j4 html' # Other input options are possible, see ProjectPythia/cookbook-actions/.github/workflows/build-book.yaml diff --git a/.github/workflows/update-resource-gallery.yaml b/.github/workflows/update-resource-gallery.yaml deleted file mode 100644 index 650e85cc9..000000000 --- a/.github/workflows/update-resource-gallery.yaml +++ /dev/null @@ -1,153 +0,0 @@ -name: Update Resource Gallery - -on: - issues: - types: - - opened - - edited - -jobs: - validate-user-submission: - if: | - github.repository == 'ProjectPythia/projectpythia.github.io' - && contains(github.event.issue.labels.*.name, 'resource-gallery-submission') - runs-on: ubuntu-latest - defaults: - run: - shell: bash -l {0} - steps: - - name: Find Comment - uses: peter-evans/find-comment@v1 - id: fc - with: - issue-number: ${{ github.event.issue.number }} - comment-author: 'github-actions[bot]' - body-includes: Thank you for your contribution - - name: Create comment - if: steps.fc.outputs.comment-id == '' - uses: peter-evans/create-or-update-comment@v1 - with: - issue-number: ${{ github.event.issue.number }} - body: | - Thank you for your contribution 🎉, @${{ github.actor }}! - - We're currently running [validation checks](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) to make sure the contents of your submission are okay. An update will be posted here shortly once the validation checks are passing. - - name: Update comment - if: steps.fc.outputs.comment-id != '' - uses: peter-evans/create-or-update-comment@v1 - with: - comment-id: ${{ steps.fc.outputs.comment-id }} - edit-mode: replace - body: | - Thank you for your contribution 🎉, @${{ github.actor }}! - - We're currently running [validation checks](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) to make sure the contents of your submission are okay. An update will be posted here shortly once the validation checks are passing. - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - - name: Install dependencies - run: | - python -m pip install pip --upgrade - python -m pip install python-frontmatter markdown-it-py pydantic[email] - - - name: Validate input - run: | - python .github/workflows/collect-user-submission.py - - - uses: actions/upload-artifact@v4 - with: - name: submission - path: resource-gallery-submission-input.json - - create-pull-request: - needs: validate-user-submission - runs-on: ubuntu-latest - defaults: - run: - shell: bash -l {0} - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - uses: actions/download-artifact@v4 - with: - name: submission - - - name: Display structure of downloaded artifacts - run: | - ls -R - - - name: Install dependencies - run: | - python -m pip install pip --upgrade - python -m pip install ruamel.yaml pre-commit - - - name: Update resource gallery - shell: python - run: | - import yaml - import json - - submission_file = 'resource-gallery-submission-input.json' - resource_gallery_file = 'portal/resource_gallery.yaml' - - with open(submission_file, 'r') as file: - new_data = json.load(file) - - with open(resource_gallery_file, 'r') as yaml_file: - existing_data = yaml.load(yaml_file, Loader=yaml.SafeLoader) or [] - - existing_data.append(new_data) - - with open(resource_gallery_file, 'w') as yaml_file: - yaml.dump(existing_data, yaml_file, default_flow_style=False) - - - name: Run pre-commit hooks - run: | - python -m pre_commit run --all-files - exit 0 - - - name: Create pull request - id: cpr - uses: peter-evans/create-pull-request@v3 - with: - commit-message: 'Update resource gallery' - committer: GitHub - author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> - signoff: false - branch: resource-gallery-${{ github.event.issue.number }} - title: 'Update resource gallery' - body: | - Update resource gallery as requested in #${{ github.event.issue.number }}. Closes #${{ github.event.issue.number }}. - - - name: Find Comment - uses: peter-evans/find-comment@v1 - id: fc - with: - issue-number: ${{ github.event.issue.number }} - comment-author: 'github-actions[bot]' - body-includes: We've created a pull request on your behalf - - - name: Create comment - if: steps.fc.outputs.comment-id == '' - uses: peter-evans/create-or-update-comment@v1 - with: - issue-number: ${{ github.event.issue.number }} - body: | - @${{ github.actor }}, your submission looks great! We've created a pull request on your behalf using the information you provided. - - The pull request can be accessed from this url: ${{ steps.cpr.outputs.pull-request-url }}. - - - name: Update comment - if: steps.fc.outputs.comment-id != '' - uses: peter-evans/create-or-update-comment@v1 - with: - comment-id: ${{ steps.fc.outputs.comment-id }} - edit-mode: replace - body: | - @${{ github.actor }}, your submission looks great! We've created a pull request on your behalf using the information you provided. - - The pull request can be accessed from this url: ${{ steps.cpr.outputs.pull-request-url }}. diff --git a/.github/workflows/write-metrics-md.py b/.github/workflows/write-metrics-md.py deleted file mode 100644 index 83a212900..000000000 --- a/.github/workflows/write-metrics-md.py +++ /dev/null @@ -1,68 +0,0 @@ -import json - - -def process_user_data(json_file, top_pages, this_year, map, md_file): - """ - Function for writing portal/metrics.md from saved files output by get-metrics.py - """ - with open(json_file, 'r') as f: - user_data = json.load(f) - - with open(md_file, 'w') as f: - f.write('# Metrics \n\n') - now = user_data['Now'] - f.write(f'Last Updated: {now}') - user_data.pop('Now') - f.write('\n\n') - - # Intro description - f.write( - 'This metrics page provides an overview of user activity collected by Google Analytics across the three pillars of Project Pythia: our portal which includes information about the project as well as our resource gallery, our Foundations book, and our Cookbooks gallery. Information is either all-time (from a pre-project start date of March 2020) or year-to-date as indicated and is updated nightly to provide real-time and automated insights into our engagement, impact, and audience reach. If you would like to request a different metrics analysis, timeframe, or view, please [open a GitHub issue](https://github.com/ProjectPythia/projectpythia.github.io/issues/new/choose).\n\n' - ) - - # Markdown table - f.write('## Table of Total Active Users by Project\n\n') - f.write( - 'This table displays the total active users of our 3 Pythia projects over the life of Project Pythia. Google analytics defines active users as the number of unique people who have visited the site and met certain [engagement requirements](https://support.google.com/analytics/answer/9234069?sjid=8697784525616937194-NC). You can read more from the [GA4 "Understand User Metrics" documentation](https://support.google.com/analytics/answer/12253918?hl=en).\n\n' - ) - headers = '| Project | All-Time Users |' - separator = '| ' + ' | '.join(['-----'] * 2) + ' |' - rows = [] - for key in user_data.keys(): - rows.append('| ' + key + ' | ' + user_data[key] + ' |') - table = '\n'.join([headers, separator] + rows) - f.write(table) - f.write('\n\n') - - # Add plots - f.write('## Chart of Active Users by Project Since Year Start\n\n') - f.write( - 'This line plot displays active users for our 3 Pythia projects (Portal in purple, Foundations in blue, and Cookbooks in salmon) since January 1st of the current year.\n\n' - ) - f.write(f'![Users this Year]({this_year})\n\n') - - f.write('## Chart of Top 5 Pages by Project\n\n') - f.write( - 'This bar-chart displays the top 5 pages by project over the life of Project Pythia, as determined by screen page views. Screen page views refers to the number of times users viewed a page, including repeated visits. To learn more visit the [GA4 "API Dimensions & Metrics" page](https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema).\n\n' - ) - f.write(f'![Top Pages]({top_pages})\n\n') - - f.write('## Map of Total Foundation Active Users by Country\n\n') - f.write( - 'This map displays the number of active users per country for Pythia Foundations for the entire life of Project Pythia.\n\n' - ) - f.write(f'![Users by Country]({map})\n\n') - - f.close() - - -if __name__ == '__main__': - json_file = 'portal/metrics/user_metrics.json' # Accessed from root repository - - # HTML is built from within `portal/` directory, so paths differ from json file. - top_pages = 'metrics/toppages.png' - this_year = 'metrics/thisyear.png' - map = 'metrics/bycountry.png' - - md_file = 'portal/metrics.md' # Written from root repository - process_user_data(json_file, top_pages, this_year, map, md_file) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 456907b8d..a2bdd9551 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,15 +15,20 @@ repos: - id: black - repo: https://github.com/keewis/blackdoc - rev: v0.4.0 + rev: v0.4.1 hooks: - id: blackdoc - repo: https://github.com/PyCQA/flake8 - rev: 7.2.0 + rev: 7.3.0 hooks: - id: flake8 + - repo: https://github.com/asottile/seed-isort-config + rev: v2.2.0 + hooks: + - id: seed-isort-config + - repo: https://github.com/PyCQA/isort rev: 6.0.1 hooks: @@ -33,4 +38,9 @@ repos: rev: 1.9.1 hooks: - id: nbqa-black + additional_dependencies: [black] - id: nbqa-pyupgrade + additional_dependencies: [pyupgrade] + exclude: foundations/quickstart.ipynb + - id: nbqa-isort + additional_dependencies: [isort] diff --git a/.prettierrc.toml b/.prettierrc.toml deleted file mode 100644 index addd6d363..000000000 --- a/.prettierrc.toml +++ /dev/null @@ -1,3 +0,0 @@ -tabWidth = 2 -semi = false -singleQuote = true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2a9429704..7d3ed1427 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,23 +1,19 @@ -# Pythia Portal contributor's guide +# Contributing to the Project Pythia Portal This document contains information specific to contributing to the -Project Pythia Portal. Please first refer to [Pythia Contributor's -Guide](https://projectpythia.org/contributing.html) for overall +Project Pythia Portal. Please first refer to [Guide for Contributing to Project Pythia](https://projectpythia.org/contributing.html) for overall contribution guidelines (such as detailed description of Project Pythia structure, forking, repository cloning, branching, etc.). ## Instructions for adding a blog post - -We use [Sphinx ABblog](https://ablog.readthedocs.io/en/stable/) to add blog posts to our site. - Within the `portal/posts/` folder add your `.md` blog file with the following heading: ``` --- -blogpost: true -date: MON DD, YYYY + +date: YYYY-MM-DD author: First Last -tags: sample-tag +tags: [sample-tag] --- ``` @@ -25,7 +21,7 @@ The post will automatically be recognized and displayed when you build the porta ## Instructions for building the portal site -The portal site is built with [Sphinx](https://www.sphinx-doc.org/). +The portal site is built with [MyST-MD](https://mystmd.org/). When testing new content it is important to build and view the site. Read the Docs automatically builds the site for you when each Pull Request is checked. However, you can also build it locally on your machine following the instructions below. @@ -73,28 +69,12 @@ _NOTE_: The `pre-commit` package is already installed via the `pythia` conda env Build the site locally using Sphinx (which you just installed in the `pythia` environment, along with all necessary dependencies): ```bash -make html +myst start --execute ``` -If this step fails and you have not updated your conda environment recently, try updating with `conda env update -f ../environment.yml` and `conda update --all` as described above. - -The newly rendered site is now available in `portal/_build/html/index.html`. -Open with your web browser, or from the terminal: +If this step fails and you may not have updated your conda environment recently, try updating with `conda env update -f ../environment.yml` and `conda update --all` as described above. -```bash -open _build/html/index.html -````` - -However, many of the links will not work. For all of the links -found in the portal to work properly, you'll need to set up a local -testing server. This can be done with Python's http.server by running -the following command from within the `portal` directory: - -```bash -python -m http.server --directory _build/html/ -``` - -and then pointing your browser at the URL: localhost:8000. +The newly rendered site is now available at [localhost:3000](http://localhost:3000). A link should appear in your terminal. More information on setting up a local test server is available from [here](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/set_up_a_local_testing_server) @@ -111,15 +91,6 @@ conda activate pythia conda update --all ``` -### Preview the built site on Netlify - -Once a pull request has passed all tests, including the `preview-site` check, the GitHub bot will post a comment with a preview link on the pull request. You can click on the link to launch a new tab with a build of the Project Pythia site. - -![CI-check](/portal/_static/images/deploy-site-CI-check.png) - -![Netlify Preview](/portal/_static/images/netlify-preview.png) - - ## Instructions for intacting with the Google Analytics API ### Setting up the Virtual Environment diff --git a/README.md b/README.md index 7ccb40f66..7c92c5150 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,6 @@ [![nightly-build](https://github.com/ProjectPythia/projectpythia.github.io/actions/workflows/nightly-build.yaml/badge.svg)](https://github.com/ProjectPythia/projectpythia.github.io/actions/workflows/nightly-build.yaml) This is the source repository for the [Project Pythia portal](https://projectpythia.org). -The portal site is built with [sphinx](https://www.sphinx-doc.org/). +The portal site is built with [MyST-MD](https://mystmd.org/). Information on contributing is available [here](CONTRIBUTING.md) diff --git a/environment.yml b/environment.yml index 64f6031d6..3ee2b07b0 100644 --- a/environment.yml +++ b/environment.yml @@ -3,12 +3,16 @@ channels: - conda-forge - nodefaults dependencies: -- ablog - matplotlib -- myst-nb - pandas - pre-commit - pyyaml -- sphinx-pythia-theme -- sphinx-design -- sphinx-copybutton +- mystmd +- jupyterlab +- cartopy +- numpy +- matplotlib +- google-api-python-client +- pip +- pip: + - google-analytics-data diff --git a/portal/Makefile b/portal/Makefile deleted file mode 100644 index d4bb2cbb9..000000000 --- a/portal/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/portal/_extensions/gallery_generator.py b/portal/_extensions/gallery_generator.py deleted file mode 100644 index 922f23652..000000000 --- a/portal/_extensions/gallery_generator.py +++ /dev/null @@ -1,184 +0,0 @@ -import itertools -import pathlib -import re - -from truncatehtml import truncate - - -def _generate_sorted_tag_keys(all_items): - key_set = set(itertools.chain(*[item['tags'].keys() for item in all_items])) - return sorted(key_set) - - -def _title_case_preserve(s): - return re.sub(r'\b(\w)', lambda m: m.group(1).upper(), s) - - -def _make_class(s): - return re.sub(r'^\d+', '', s.replace(' ', '-').lower()) - - -def _generate_tag_set(all_items, tag_key=None): - tag_set = set() - for item in all_items: - for k, e in item['tags'].items(): - tags = [_title_case_preserve(t) for t in e] - if tag_key and k != tag_key: - continue - for t in tags: - tag_set.add(t) - - return tag_set - - -def _generate_tag_menu(all_items, tag_key): - tag_set = _generate_tag_set(all_items, tag_key) - tag_list = sorted(tag_set) - - options = ''.join( - f'
  • ' - for tag in tag_list - ) - - return f""" - :::{{dropdown}} {tag_key} - - ::: - """ - - -def generate_menu(all_items, submit_btn_txt=None, submit_btn_link=None): - key_list = _generate_sorted_tag_keys(all_items) - - menu_html = '
    \n' - menu_html += '\n' - menu_html += '
    \n' - menu_html += '
    \n' - for tag_key in key_list: - menu_html += _generate_tag_menu(all_items, tag_key) + '\n' - menu_html += '
    \n' - menu_html += '
    \n' - menu_html += '\n' - return menu_html - - -def build_from_items(items, filename, title='Gallery', subtitle=None, subtext=None, menu_html='', max_descr_len=300): - # Build the gallery file - grid_body = [] - for item in items: - if not item.get('thumbnail'): - item['thumbnail'] = '_static/images/ebp-logo.png' - thumbnail = item['thumbnail'][1:] if item['thumbnail'].startswith('/') else item['thumbnail'] - tag_list = sorted((itertools.chain(*item['tags'].values()))) - tag_list_f = [tag.replace(' ', '-') for tag in tag_list] - - tags = [f'{_title_case_preserve(tag)}' for tag in tag_list_f] - tags = '\n'.join(tags) - tag_classes = ' '.join(tag_list_f) - - author_strs = set() - affiliation_strs = set() - for a in item['authors']: - author_name = a.get('name', 'Anonymous') - author_email = a.get('email', None) - if author_email: - _str = f'{author_name}' - else: - _str = author_name - author_strs.add(_str) - - affiliation_name = a.get('affiliation', None) - if affiliation_name: - affiliation_url = a.get('affiliation_url', None) - if affiliation_url: - _str = f'{affiliation_name}' - else: - _str = affiliation_name - affiliation_strs.add(_str) - - authors_str = f"Author: {', '.join(author_strs)}" - if affiliation_strs: - affiliations_str = f"Affiliation: {' '.join(affiliation_strs)}" - else: - affiliations_str = '' - - ellipsis_str = ' ... more' - short_description = truncate(item['description'], max_descr_len, ellipsis=ellipsis_str) - - if ellipsis_str in short_description: - modal_str = f""" - - """ - modal_str = '\n'.join([m.lstrip() for m in modal_str.split('\n')]) - else: - modal_str = '' - - new_card = f""" - :::{{grid-item-card}} - :shadow: md - :class-footer: card-footer - :class-card: tagged-card {tag_classes} - - - {modal_str} - - +++ - - {tags} - - ::: - - """ - - grid_body.append('\n'.join([m.lstrip() for m in new_card.split('\n')])) - - stitle = f'#### {subtitle}' if subtitle else '' - stext = subtext if subtext else '' - - grid_body = '\n'.join(grid_body) - grid = f"""\ - {title} - {'=' * len(title)} - - {stitle} - {stext} - - {menu_html} - - ::::{{grid}} 1 - :gutter: 0 - - {grid_body} - - - - """ - - grid = '\n'.join([m.lstrip() for m in grid.split('\n')]) - - pathlib.Path(f'{filename}.md').write_text(grid) diff --git a/portal/_extensions/resource_gallery_generator.py b/portal/_extensions/resource_gallery_generator.py deleted file mode 100644 index 9f47a2d3e..000000000 --- a/portal/_extensions/resource_gallery_generator.py +++ /dev/null @@ -1,17 +0,0 @@ -import yaml -from gallery_generator import build_from_items, generate_menu - - -def main(app): - with open('resource_gallery.yaml') as fid: - all_items = yaml.safe_load(fid) - - title = 'Resource Gallery' - submit_btn_link = 'https://github.com/ProjectPythia/projectpythia.github.io/issues/new?assignees=&labels=resource-gallery-submission&template=update-resource-gallery.md&title=' - submit_btn_txt = 'Submit a new resource' - menu_html = generate_menu(all_items, submit_btn_txt=submit_btn_txt, submit_btn_link=submit_btn_link) - build_from_items(all_items, 'resource-gallery', title=title, menu_html=menu_html) - - -def setup(app): - app.connect('builder-inited', main) diff --git a/portal/_extensions/truncatehtml.py b/portal/_extensions/truncatehtml.py deleted file mode 100644 index 9a6f63f54..000000000 --- a/portal/_extensions/truncatehtml.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2015 Eric Entzel - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from __future__ import print_function - -END = -1 - -# HTML5 void-elements that do not require a closing tag -# https://html.spec.whatwg.org/multipage/syntax.html#void-elements -VOID_ELEMENTS = ( - 'area', - 'base', - 'br', - 'col', - 'embed', - 'hr', - 'img', - 'input', - 'link', - 'meta', - 'param', - 'source', - 'track', - 'wbr', -) - - -class UnbalancedError(Exception): - pass - - -class OpenTag: - def __init__(self, tag, rest=''): - self.tag = tag - self.rest = rest - - def as_string(self): - return '<' + self.tag + self.rest + '>' - - -class CloseTag(OpenTag): - def as_string(self): - return '' - - -class SelfClosingTag(OpenTag): - pass - - -class Tokenizer: - def __init__(self, input): - self.input = input - self.counter = 0 # points at the next unconsumed character of the input - - def __next_char(self): - self.counter += 1 - return self.input[self.counter] - - def next_token(self): - try: - char = self.input[self.counter] - self.counter += 1 - if char == '&': - return self.__entity() - elif char != '<': - return char - elif self.input[self.counter] == '/': - self.counter += 1 - return self.__close_tag() - else: - return self.__open_tag() - except IndexError: - return END - - def __entity(self): - """Return a token representing an HTML character entity. - Precondition: self.counter points at the charcter after the & - Postcondition: self.counter points at the character after the ; - """ - next_char = self.input[self.counter + 1] - if next_char == ' ': - return '&' - - char = self.input[self.counter] - entity = ['&'] - while char != ';': - entity.append(char) - char = self.__next_char() - entity.append(';') - self.counter += 1 - return ''.join(entity) - - def __open_tag(self): - """Return an open/close tag token. - Precondition: self.counter points at the first character of the tag name - Postcondition: self.counter points at the character after the - """ - char = self.input[self.counter] - tag = [] - rest = [] - while char != '>' and char != ' ': - tag.append(char) - char = self.__next_char() - while char != '>': - rest.append(char) - char = self.__next_char() - if self.input[self.counter - 1] == '/': - self.counter += 1 - return SelfClosingTag(''.join(tag), ''.join(rest)) - elif ''.join(tag) in VOID_ELEMENTS: - self.counter += 1 - return SelfClosingTag(''.join(tag), ''.join(rest)) - else: - self.counter += 1 - return OpenTag(''.join(tag), ''.join(rest)) - - def __close_tag(self): - """Return an open/close tag token. - Precondition: self.counter points at the first character of the tag name - Postcondition: self.counter points at the character after the - """ - char = self.input[self.counter] - tag = [] - while char != '>': - tag.append(char) - char = self.__next_char() - self.counter += 1 - return CloseTag(''.join(tag)) - - -def truncate(str, target_len, ellipsis=''): - """Returns a copy of str truncated to target_len characters, - preserving HTML markup (which does not count towards the length). - Any tags that would be left open by truncation will be closed at - the end of the returned string. Optionally append ellipsis if - the string was truncated.""" - stack = [] # open tags are pushed on here, then popped when the matching close tag is found - retval = [] # string to be returned - length = 0 # number of characters (not counting markup) placed in retval so far - tokens = Tokenizer(str) - tok = tokens.next_token() - while tok != END: - if length >= target_len and tok == ' ': - retval.append(ellipsis) - break - if tok.__class__.__name__ == 'OpenTag': - stack.append(tok) - retval.append(tok.as_string()) - elif tok.__class__.__name__ == 'CloseTag': - if stack[-1].tag == tok.tag: - stack.pop() - retval.append(tok.as_string()) - else: - raise UnbalancedError(tok.as_string()) - elif tok.__class__.__name__ == 'SelfClosingTag': - retval.append(tok.as_string()) - else: - retval.append(tok) - length += 1 - tok = tokens.next_token() - while len(stack) > 0: - tok = CloseTag(stack.pop().tag) - retval.append(tok.as_string()) - return ''.join(retval) diff --git a/portal/_static/custom.css b/portal/_static/custom.css deleted file mode 100644 index 4b1f72078..000000000 --- a/portal/_static/custom.css +++ /dev/null @@ -1,241 +0,0 @@ -:root { - --pst-color-border: rgba(0, 0, 0, 0.125) !important; -} - -.bd-main .bd-content .bd-article-container { - max-width: 100%; - /* default is 60em */ -} - -.bd-page-width { - max-width: 100%; - /* default is 88rem */ -} - -.sd-card-footer { - background: rgba(var(--spt-color-gray-100), 1) !important; - padding: 4px; -} - -main.banner-main #project-pythia { - padding-top: 1rem; - padding-bottom: 1rem; -} - -main.banner-main #project-pythia p { - font-size: 1.4rem; - /* default: 1.25rem * / - /* font-weight: 700; default: 300 */ -} - -main.banner-main #project-pythia a, -main.banner-main #project-pythia a:visited { - color: rgba(var(--spt-color-light), 1); - text-decoration: underline dotted rgba(var(--spt-color-gray-400), 1); -} - -main.banner-main #project-pythia a.headerlink:hover { - color: #DDD; -} - -main.banner-main #project-pythia a.btn-light { - color: rgba(var(--pst-color-primary), 1) -} - -.modal { - display: none; - position: fixed; - background: #f8f9fa; - border-radius: 5px; - padding: 3rem; - width: calc(100% - 8rem); - height: auto !important; - max-height: calc(100% - 8rem); - overflow: scroll; - top: 4rem; - left: 4rem; - z-index: 20001; -} - -.modal-backdrop { - display: none; - position: fixed; - background: rgba(0, 0, 0, 0.5); - top: 0; - left: 0; - height: 100vh; - width: 100vw; - z-index: 20000; -} - -.modal-btn { - color: #1a658f; - text-decoration: none; -} - -.modal-img { - float: right; - margin: 0 0 2rem 2rem; - max-width: 260px; - max-height: 260px; -} - -.gallery-menu { - margin-bottom: 1rem; -} - -.gallery-card div.container { - padding: 0 0 0 1rem; -} - -.gallery-thumbnail { - display: block; - float: left; - margin: auto 0; - padding: 0; - max-width: 160px; - background: transparent !important; -} - -.card-subtitle { - font-size: 0.8rem; -} - -.my-2 { - color: inherit; -} - -.text-decoration-none { - text-decoration: none; - color: inherit; -} - -@media (max-width: 576px) { - .modal { - padding: 2rem; - width: calc(100% - 4rem); - max-height: calc(100% - 4rem); - top: 2rem; - left: 2rem; - } - - .modal-img { - display: none; - } - - .gallery-card { - flex-direction: column; - } - - .gallery-thumbnail { - float: none; - margin: 0 0 1rem 0; - max-width: 100%; - } - - .gallery-card div.container { - padding: 0; - } - - .gallery-return-btn { - padding-bottom: 1rem; - } -} - -div.horizontalgap { - float: left; - overflow: hidden; - height: 1px; - width: 0px; -} - -.dropdown ul { - list-style: none; - padding: 0; - margin: 0; -} - -.dropdown-item { - display: block; -} - -.dropdown-item input[type="checkbox"] { - margin-right: 0.5em; -} - -details.sd-dropdown { - box-shadow: none !important; -} - -details.sd-dropdown summary.sd-card-header+div.sd-summary-content { - background-color: white !important; - border: 0.2rem solid var(--pst-sd-dropdown-color) !important; - border-radius: calc(.25rem - 1px); -} - -.sd-summary-content.sd-card-body.docutils { - position: absolute; - z-index: 100; -} - -details.sd-dropdown:not([open])>.sd-card-header { - background-color: white !important; - border: 2px solid #1a648f !important; - color: #1a648f; - border-radius: .5rem; -} - -details.sd-dropdown[open]>.sd-card-header { - background-color: #1a648f !important; - color: white; - border-radius: .5rem; -} - -p { - color: black; -} - -.sd-col.sd-d-flex-row.docutils.has-visible-card { - margin-bottom: 1rem; -} - -/* Set a light color for text throughout in dark mode */ -html[data-theme="dark"] body { - color: #e0e0e0; - /* Light gray */ -} - -/* Ensure links are visible */ -html[data-theme="dark"] a { - color: #b0c4de; - /* Light blue */ -} - -/* Target specific elements that need adjustment */ -html[data-theme="dark"] .sd-card-body.docutils { - background-color: #202020; - /* Darker gray background for cards */ - color: #e0e0e0; - /* Light gray text in cards */ -} - -/* Adjust headings if necessary */ -html[data-theme="dark"] h1, -html[data-theme="dark"] h2, -html[data-theme="dark"] h3, -html[data-theme="dark"] h4, -html[data-theme="dark"] h5, -html[data-theme="dark"] h6 { - color: #f5f5f5; - /* Almost white for headings */ -} - -.admonition.large-banner { - background-color: white; - border: 2px solid #b8860b; - color: #222; -} - -.admonition :last-child, div.admonition :last-child { - margin-left: .5rem; -} diff --git a/portal/_static/custom.js b/portal/_static/custom.js deleted file mode 100644 index 139a1454c..000000000 --- a/portal/_static/custom.js +++ /dev/null @@ -1,99 +0,0 @@ -function getClassOfCheckedCheckboxes(checkboxes) { - var tags = []; - checkboxes.forEach(function (cb) { - if (cb.checked) { - tags.push(cb.getAttribute("rel")); - } - }); - return tags; - } - - function change() { - console.log("Change event fired."); - var affiliationsCbs = document.querySelectorAll(".affiliation input[type='checkbox']"); - var domainsCbs = document.querySelectorAll(".domains input[type='checkbox']"); - var formatsCbs = document.querySelectorAll(".formats input[type='checkbox']"); - var packagesCbs = document.querySelectorAll(".packages input[type='checkbox']"); - - var affiliatioinTags = getClassOfCheckedCheckboxes(affiliationsCbs); - var domainTags = getClassOfCheckedCheckboxes(domainsCbs); - var formatTags = getClassOfCheckedCheckboxes(formatsCbs); - var packageTags = getClassOfCheckedCheckboxes(packagesCbs); - - var filters = { - affiliations: affiliatioinTags, - domains: domainTags, - formats: formatTags, - packages: packageTags - }; - - filterResults(filters); - } - - function filterResults(filters) { - console.log("Filtering results..."); - var rElems = document.querySelectorAll(".tagged-card"); - - rElems.forEach(function (el) { - var isVisible = true; // Assume visible by default - - // Check if the element has any domain or package filter - if (filters.affiliations.length > 0 || filters.domains.length > 0 || filters.formats.length > 0 || filters.packages.length > 0) { - var hasMatchingAffiliation = filters.affiliations.length === 0 || filters.affiliations.some(affiliation => el.classList.contains(affiliation)); - var hasMatchingDomain = filters.domains.length === 0 || filters.domains.some(domain => el.classList.contains(domain)); - var hasMatchingFormat = filters.formats.length === 0 || filters.formats.some(format => el.classList.contains(format)); - var hasMatchingPackage = filters.packages.length === 0 || filters.packages.some(package => el.classList.contains(package)); - - // The element should be visible if it matches any filter within each category - isVisible = hasMatchingAffiliation && hasMatchingDomain && hasMatchingFormat && hasMatchingPackage; - } - - // Toggle visibility based on the result - if (isVisible) { - el.classList.remove("d-none"); - el.classList.add("d-flex"); - } else { - el.classList.remove("d-flex"); - el.classList.add("d-none"); - } - }); - - // Update the margins after filtering - updateMargins(); - } - - var checkboxes = document.querySelectorAll('input[type="checkbox"]'); - checkboxes.forEach(function (checkbox) { - checkbox.addEventListener("change", change); - }); - - function updateMargins() { - const columns = document.querySelectorAll('.sd-col.sd-d-flex-row.docutils'); - - columns.forEach(column => { - // Check if this column has any visible cards - const hasVisibleCard = Array.from(column.children).some(child => !child.classList.contains('d-none')); - - // Toggle a class based on whether there are visible cards - if (hasVisibleCard) { - column.classList.add('has-visible-card'); - } else { - column.classList.remove('has-visible-card'); - } - }); - } - - function clearCbs() { - // Select all checkbox inputs and uncheck them - var checkboxes = document.querySelectorAll('input[type="checkbox"]'); - checkboxes.forEach(function(checkbox) { - checkbox.checked = false; - }); - - change(); - } - - // Initial call to set up correct margins when the page loads - document.addEventListener('DOMContentLoaded', updateMargins); - - console.log("Script loaded."); diff --git a/portal/_static/thumbnails/DC_logo_vision.png b/portal/_static/thumbnails/DC_logo_vision.png deleted file mode 100644 index e6b2f3cd5..000000000 Binary files a/portal/_static/thumbnails/DC_logo_vision.png and /dev/null differ diff --git a/portal/_static/thumbnails/Enthought.png b/portal/_static/thumbnails/Enthought.png deleted file mode 100644 index 552ef1ef3..000000000 Binary files a/portal/_static/thumbnails/Enthought.png and /dev/null differ diff --git a/portal/_static/thumbnails/ProjectPythia_Blue.png b/portal/_static/thumbnails/ProjectPythia_Blue.png deleted file mode 100644 index 8d49fc24e..000000000 Binary files a/portal/_static/thumbnails/ProjectPythia_Blue.png and /dev/null differ diff --git a/portal/_static/thumbnails/Xdev.png b/portal/_static/thumbnails/Xdev.png deleted file mode 100644 index 9d9a7960b..000000000 Binary files a/portal/_static/thumbnails/Xdev.png and /dev/null differ diff --git a/portal/_static/thumbnails/arm_logo.png b/portal/_static/thumbnails/arm_logo.png deleted file mode 100644 index 8b95ec19d..000000000 Binary files a/portal/_static/thumbnails/arm_logo.png and /dev/null differ diff --git a/portal/_static/thumbnails/british-columbia.jpg b/portal/_static/thumbnails/british-columbia.jpg deleted file mode 100644 index 27c1883c1..000000000 Binary files a/portal/_static/thumbnails/british-columbia.jpg and /dev/null differ diff --git a/portal/_static/thumbnails/cartopy.png b/portal/_static/thumbnails/cartopy.png deleted file mode 100644 index 08b5f6185..000000000 Binary files a/portal/_static/thumbnails/cartopy.png and /dev/null differ diff --git a/portal/_static/thumbnails/climatematch.png b/portal/_static/thumbnails/climatematch.png deleted file mode 100644 index 6dc65085a..000000000 Binary files a/portal/_static/thumbnails/climatematch.png and /dev/null differ diff --git a/portal/_static/thumbnails/climlablogo.png b/portal/_static/thumbnails/climlablogo.png deleted file mode 100644 index eb2cf42e7..000000000 Binary files a/portal/_static/thumbnails/climlablogo.png and /dev/null differ diff --git a/portal/_static/thumbnails/cmip6.png b/portal/_static/thumbnails/cmip6.png deleted file mode 100644 index ce8d71052..000000000 Binary files a/portal/_static/thumbnails/cmip6.png and /dev/null differ diff --git a/portal/_static/thumbnails/dask.png b/portal/_static/thumbnails/dask.png deleted file mode 100644 index 06e61e6f6..000000000 Binary files a/portal/_static/thumbnails/dask.png and /dev/null differ diff --git a/portal/_static/thumbnails/eScience_Logo_HR.png b/portal/_static/thumbnails/eScience_Logo_HR.png deleted file mode 100644 index f33597794..000000000 Binary files a/portal/_static/thumbnails/eScience_Logo_HR.png and /dev/null differ diff --git a/portal/_static/thumbnails/earth-lab-logo.png b/portal/_static/thumbnails/earth-lab-logo.png deleted file mode 100644 index 453658486..000000000 Binary files a/portal/_static/thumbnails/earth-lab-logo.png and /dev/null differ diff --git a/portal/_static/thumbnails/earth_env_data_science.png b/portal/_static/thumbnails/earth_env_data_science.png deleted file mode 100644 index 78fdf0dcf..000000000 Binary files a/portal/_static/thumbnails/earth_env_data_science.png and /dev/null differ diff --git a/portal/_static/thumbnails/geocat.png b/portal/_static/thumbnails/geocat.png deleted file mode 100644 index 81552571f..000000000 Binary files a/portal/_static/thumbnails/geocat.png and /dev/null differ diff --git a/portal/_static/thumbnails/geopandas_icon.png b/portal/_static/thumbnails/geopandas_icon.png deleted file mode 100644 index 234ea9542..000000000 Binary files a/portal/_static/thumbnails/geopandas_icon.png and /dev/null differ diff --git a/portal/_static/thumbnails/hvplot-wm.png b/portal/_static/thumbnails/hvplot-wm.png deleted file mode 100644 index 9add7d77b..000000000 Binary files a/portal/_static/thumbnails/hvplot-wm.png and /dev/null differ diff --git a/portal/_static/thumbnails/matplotlib.svg b/portal/_static/thumbnails/matplotlib.svg deleted file mode 100644 index 6e4f4f49c..000000000 --- a/portal/_static/thumbnails/matplotlib.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/portal/_static/thumbnails/metpy-mondays.jpeg b/portal/_static/thumbnails/metpy-mondays.jpeg deleted file mode 100644 index 2c76421c7..000000000 Binary files a/portal/_static/thumbnails/metpy-mondays.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/metpy.jpg b/portal/_static/thumbnails/metpy.jpg deleted file mode 100644 index f570c8fd1..000000000 Binary files a/portal/_static/thumbnails/metpy.jpg and /dev/null differ diff --git a/portal/_static/thumbnails/numpy.png b/portal/_static/thumbnails/numpy.png deleted file mode 100644 index 3bef101f2..000000000 Binary files a/portal/_static/thumbnails/numpy.png and /dev/null differ diff --git a/portal/_static/thumbnails/pandas.png b/portal/_static/thumbnails/pandas.png deleted file mode 100644 index 46ec0f654..000000000 Binary files a/portal/_static/thumbnails/pandas.png and /dev/null differ diff --git a/portal/_static/thumbnails/plotting_earth_science.png b/portal/_static/thumbnails/plotting_earth_science.png deleted file mode 100644 index f0dde3d99..000000000 Binary files a/portal/_static/thumbnails/plotting_earth_science.png and /dev/null differ diff --git a/portal/_static/thumbnails/portland-state.png b/portal/_static/thumbnails/portland-state.png deleted file mode 100644 index da6308b1c..000000000 Binary files a/portal/_static/thumbnails/portland-state.png and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-builtinpackage.jpeg b/portal/_static/thumbnails/ptss-builtinpackage.jpeg deleted file mode 100644 index c79bf3a71..000000000 Binary files a/portal/_static/thumbnails/ptss-builtinpackage.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-cartopy.jpeg b/portal/_static/thumbnails/ptss-cartopy.jpeg deleted file mode 100644 index 9ac30119b..000000000 Binary files a/portal/_static/thumbnails/ptss-cartopy.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-dask1.jpeg b/portal/_static/thumbnails/ptss-dask1.jpeg deleted file mode 100644 index d22bdd4c0..000000000 Binary files a/portal/_static/thumbnails/ptss-dask1.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-dask2.jpeg b/portal/_static/thumbnails/ptss-dask2.jpeg deleted file mode 100644 index bb7805dd9..000000000 Binary files a/portal/_static/thumbnails/ptss-dask2.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-datadict.jpeg b/portal/_static/thumbnails/ptss-datadict.jpeg deleted file mode 100644 index cbbd4da86..000000000 Binary files a/portal/_static/thumbnails/ptss-datadict.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-firstpackage.jpeg b/portal/_static/thumbnails/ptss-firstpackage.jpeg deleted file mode 100644 index ff3b8d1fe..000000000 Binary files a/portal/_static/thumbnails/ptss-firstpackage.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-geocatcomp.jpeg b/portal/_static/thumbnails/ptss-geocatcomp.jpeg deleted file mode 100644 index 70848a048..000000000 Binary files a/portal/_static/thumbnails/ptss-geocatcomp.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-geocatplot.jpeg b/portal/_static/thumbnails/ptss-geocatplot.jpeg deleted file mode 100644 index 632b9581d..000000000 Binary files a/portal/_static/thumbnails/ptss-geocatplot.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-git.png b/portal/_static/thumbnails/ptss-git.png deleted file mode 100644 index 70f8dad00..000000000 Binary files a/portal/_static/thumbnails/ptss-git.png and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-jupyter.jpeg b/portal/_static/thumbnails/ptss-jupyter.jpeg deleted file mode 100644 index 4232e3ad5..000000000 Binary files a/portal/_static/thumbnails/ptss-jupyter.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-matplotlib.jpeg b/portal/_static/thumbnails/ptss-matplotlib.jpeg deleted file mode 100644 index 010d3d6da..000000000 Binary files a/portal/_static/thumbnails/ptss-matplotlib.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-numpy.jpeg b/portal/_static/thumbnails/ptss-numpy.jpeg deleted file mode 100644 index 7df8748b2..000000000 Binary files a/portal/_static/thumbnails/ptss-numpy.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-oop.jpeg b/portal/_static/thumbnails/ptss-oop.jpeg deleted file mode 100644 index f4c847b18..000000000 Binary files a/portal/_static/thumbnails/ptss-oop.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-pandas.jpeg b/portal/_static/thumbnails/ptss-pandas.jpeg deleted file mode 100644 index e6ed06f9b..000000000 Binary files a/portal/_static/thumbnails/ptss-pandas.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-txtfile.jpeg b/portal/_static/thumbnails/ptss-txtfile.jpeg deleted file mode 100644 index 1e9585acf..000000000 Binary files a/portal/_static/thumbnails/ptss-txtfile.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-writingfx.jpeg b/portal/_static/thumbnails/ptss-writingfx.jpeg deleted file mode 100644 index 73922e0d6..000000000 Binary files a/portal/_static/thumbnails/ptss-writingfx.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-xarray1.jpeg b/portal/_static/thumbnails/ptss-xarray1.jpeg deleted file mode 100644 index b0146c37d..000000000 Binary files a/portal/_static/thumbnails/ptss-xarray1.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/ptss-xarray2.jpeg b/portal/_static/thumbnails/ptss-xarray2.jpeg deleted file mode 100644 index 1b832bcf4..000000000 Binary files a/portal/_static/thumbnails/ptss-xarray2.jpeg and /dev/null differ diff --git a/portal/_static/thumbnails/rasterio-tutorial.png b/portal/_static/thumbnails/rasterio-tutorial.png deleted file mode 100644 index 6d2c2b492..000000000 Binary files a/portal/_static/thumbnails/rasterio-tutorial.png and /dev/null differ diff --git a/portal/_static/thumbnails/siphon.png b/portal/_static/thumbnails/siphon.png deleted file mode 100644 index b66d071ac..000000000 Binary files a/portal/_static/thumbnails/siphon.png and /dev/null differ diff --git a/portal/_static/thumbnails/unidata_150x150.png b/portal/_static/thumbnails/unidata_150x150.png deleted file mode 100644 index 9ddd2bea0..000000000 Binary files a/portal/_static/thumbnails/unidata_150x150.png and /dev/null differ diff --git a/portal/_static/thumbnails/xarray.png b/portal/_static/thumbnails/xarray.png deleted file mode 100644 index a38fcdbb1..000000000 Binary files a/portal/_static/thumbnails/xarray.png and /dev/null differ diff --git a/portal/_static/thumbnails/zero-to-pandas.png b/portal/_static/thumbnails/zero-to-pandas.png deleted file mode 100644 index 15afc97f5..000000000 Binary files a/portal/_static/thumbnails/zero-to-pandas.png and /dev/null differ diff --git a/portal/_templates/footer-extra.html b/portal/_templates/footer-extra.html deleted file mode 100644 index 52e29e301..000000000 --- a/portal/_templates/footer-extra.html +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/portal/_templates/footer-menu.html b/portal/_templates/footer-menu.html deleted file mode 100644 index 919cc9c36..000000000 --- a/portal/_templates/footer-menu.html +++ /dev/null @@ -1,32 +0,0 @@ - {% set ext_icon = '' %} - diff --git a/portal/blog.md b/portal/blog.md deleted file mode 100644 index f62f46fdf..000000000 --- a/portal/blog.md +++ /dev/null @@ -1,3 +0,0 @@ -# Blog - -This will be replaced by `ablog` so there's nothing here. diff --git a/portal/conf.py b/portal/conf.py deleted file mode 100644 index 38e60ddc2..000000000 --- a/portal/conf.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import shutil -import sys - -sys.path.insert(0, os.path.abspath('_extensions')) - - -# -- Project information ----------------------------------------------------- - -project = 'Project Pythia' -author = 'the Project Pythia Community' -copyright = '2024' - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'resource_gallery_generator', - 'ablog', - 'myst_nb', - 'sphinx_copybutton', - 'sphinx_design', - 'sphinx.ext.intersphinx', -] - -# Define what extensions will parse which kind of source file -source_suffix = { - '.ipynb': 'myst-nb', - '.myst': 'myst-nb', -} - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'sphinx_pythia_theme' -html_last_updated_fmt = '%-d %B %Y' - -# Logo & Title -html_logo = '_static/images/logos/pythia_logo-white-rtext.svg' -html_title = '' - -# Favicon -html_favicon = '_static/images/icons/favicon.ico' - -# Permalinks Icon -html_permalinks_icon = '' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] -html_css_files = ['custom.css'] -html_js_files = ['custom.js'] - -# Disable Sidebars on special pages -html_sidebars = { - 'index': [], - 'resource-gallery': [], -} - -# HTML Theme-specific Options -html_theme_options = { - 'analytics': { - 'google_analytics_id': 'G-T9KGMX7VHZ', - }, - 'github_url': 'https://github.com/ProjectPythia', - 'twitter_url': 'https://twitter.com/project_pythia', - 'icon_links': [ - { - 'name': 'YouTube', - 'url': 'https://www.youtube.com/channel/UCoZPBqJal5uKpO8ZiwzavCw', - 'icon': 'fab fa-youtube-square', - 'type': 'fontawesome', - } - ], - 'logo': { - 'link': 'https://projectpythia.org', - }, - 'navbar_align': 'left', - 'navbar_start': ['navbar-logo'], - 'navbar_links': [ - {'name': 'Home', 'url': 'https://projectpythia.org'}, - {'name': 'Foundations', 'url': 'https://foundations.projectpythia.org'}, - {'name': 'Cookbooks', 'url': 'https://cookbooks.projectpythia.org/'}, - {'name': 'Resources', 'url': 'https://projectpythia.org/resource-gallery.html'}, - {'name': 'Community', 'url': 'https://projectpythia.org/#join-us'}, - {'name': 'Blog', 'url': 'https://projectpythia.org/blog'}, - ], - 'navbar_end': ['navbar-icon-links'], - 'page_layouts': { - 'index': 'page-banner.html', - 'resource-gallery': 'page-standalone.html', - }, - 'footer_logos': { - 'NSF NCAR': '_static/images/logos/NSF-NCAR_Lockup-UCAR-Dark_102523.png', - 'NSF Unidata': '_static/images/logos/NSF-Unidata_lockup_horizontal_2023.png', - 'UAlbany': '_static/images/logos/UAlbany-A2-logo-purple-gold.svg', - }, - 'footer_start': ['footer-logos', 'footer-menu', 'footer-info', 'footer-extra'], -} - -# MyST config -myst_enable_extensions = ['amsmath', 'colon_fence', 'deflist', 'html_image'] -myst_url_schemes = ['http', 'https', 'mailto'] -nb_execution_mode = 'off' -myst_heading_anchors = 3 - -# List of patterns for linkcheck to skip if broken -linkcheck_ignore = [ - # DOI are immutable and presumed generally functional - r'https://doi\.org/.*', - # Stackoverflow 403 on GHA - r'https://stackoverflow\.com/.*', -] - -# CUSTOM SCRIPTS ============================================================== - -# Copy root files into content pages ------------------------------------------ - -shutil.copyfile('../CODEOFCONDUCT.md', 'code_of_conduct.md') - -# Blog configuration settings - -blog_post_pattern = ['posts/*.rst', 'posts/*.md'] diff --git a/portal/contributing.md b/portal/contributing.md index 7b1cfce8e..1cffc928a 100644 --- a/portal/contributing.md +++ b/portal/contributing.md @@ -1,6 +1,6 @@ -# Project Pythia Contributor's Guide +# Contributing to Project Pythia -```{Note} +```{note} This the top-level guide for Project Pythia and a great starting point for getting involved! We also have specific guides for @@ -10,7 +10,7 @@ and [contributing new Cookbooks](https://projectpythia.org/cookbook-guide.html). ## Overview -Welcome! This is the main contributors guide for Project Pythia. +Welcome! This is the main guide for contributing to Project Pythia. Project Pythia is an open community, and all contributions are welcome following our [Code of Conduct](https://projectpythia.org/code_of_conduct.html). All @@ -18,8 +18,7 @@ of the content for Project Pythia is hosted on GitHub in a number of different public repositories. From this document you can learn about the many ways that you can contribute to this community project, and where you can find additional information. In many -cases pertinent details may be found in repository-specific -contributors guides **that should always be consulted in addition +cases pertinent details may be found in repository-specific guides **that should always be consulted in addition to this guide**. More on this topic later. ## The many ways to contribute @@ -72,7 +71,7 @@ is a collection of material that the Pythia team believes is essential knowledge for geoscientists to effectively use the Scientific Python Ecosystem. The Pythia Foundations content is hosted on a separate [GitHub repo](https://github.com/ProjectPythia/pythia-foundations), -and contributors should consult the contributor’s guide there for +and contributors should consult the [Contributing to Foundations](https://foundations.projectpythia.org/appendix/how-to-contribute) guide there for information specific to Foundations. However, adding new, or changing existing Foundations content requires contributors to be familiar with a few technologies and workflows, described in Advanced @@ -88,7 +87,7 @@ specific problem. Typically, a cookbook references material presented elsewhere in Project Pythia, such as [Pythia Foundations](https://foundations.projectpythia.org). Each Cookbook is hosted in a separate GitHub repo under the umbrella [Project Pythia organization](https://github.com/ProjectPythia). -Contributors should consult the [Cookbook-specific Contributor's guide](/cookbook-guide). +Contributors should consult the [Contributing a New Cookbook](/cookbook-guide) guide. ### Pythia Portal @@ -114,10 +113,10 @@ process of setting up GitHub/Git, installing and configuring conda, and submitting a PR. Note, while this information is common to all Pythia repositories, repo-specific information might be required. -In all cases contributors should consult the repo-specific contributor’s +In all cases contributors should consult the repo-specific contribution guide for their target repository. -Repo-specific guides can be found in the file named `CONTRIBUTING.md`, located in the top level directory of each repository, or by clicking on the menu item labeled “Contributor’s Guide” found in the rendered content generated by each repository. +Repo-specific guides can be found in the file named `CONTRIBUTING.md`, located in the top level directory of most repositories, or by clicking on the appropriately named menu item on each site. Note that the "Contributing a new Cookbook" guide is found in the Pythia portal (rather than in any one specific Cookbook). Lastly, much of this information has been co-opted from the [GeoCAT project](https://geocat.ucar.edu). @@ -143,7 +142,7 @@ command line tool for collaborative software version control, while GitHub is an online, web-accessible service that greatly simplifies using the powerful, yet often complex, Git. -```{Note} +```{note} GitHub operates entirely within a web browser. You do not need to install anything, but you will need to set up a free GitHub account. Git is a command line tool that is most likely already @@ -221,12 +220,12 @@ desktop or laptop. Before using your conda environment to work on Pythia content, you'll need to perform an addtional one-time setup that is specific to each Pythia repo. After the one-time configuration is complete you will need to "activate" a repo-specific environment whenever -you wish to use it. Consult the repo-specific contributor’s guide +you wish to use it. Consult the repo-specific contribution guide for information on “Building the site”, and follow the steps described therein. -```{Note} -Repository-specific contributor's +```{note} +Repository-specific contribution information can always be accessed by navigating your web browser to the appropriate Project Pythia GitHub repository, [here](https://github.com/ProjectPythia/pythia-foundations) for @@ -326,7 +325,7 @@ on this later. For further information see the [GitHub docs on forking a repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo). -```{Note} +```{note} At this point you should have a local copy of the repository in your current working directory. You can safely make changes to any of the contents. Once you are ready to contribute your changes @@ -337,7 +336,7 @@ back to the Pythia repository you will need to submit a Pull Request ### Make your local changes At this point you should be able to make your desired changes to -the local copies of your files. **Always consult the repo-specific contributor’s +the local copies of your files. **Always consult the repo-specific contribution guide for information specific to the repo you are working on.** ### Submit a Pull Request (PR) @@ -390,7 +389,7 @@ $ git commit PATH_TO_NEW_FILE Which will prompt you for a log message. Please provide something informative. If you make lots of changes, it is best to make multiple commits, broken up into related chunks. E.g. “fixed x”, “added documentation”, “added testing”. -```{Note} +```{note} When executing `git commit` after `git add PATH_TO_NEW_FILE`, specifying the path to the new file isn't stricly necessary. However, in other instances the file path argument is required. We include it @@ -411,7 +410,7 @@ $ git status after your `commit` to verify everything looks as expected. -```{Note} +```{note} `pre-commit` _hooks_ can sometimes be difficult to satisfy. You can always tell **git** not to run the _hook_ by running `git commit --no-verify`. diff --git a/portal/cookbook-guide.md b/portal/cookbook-guide.md index 2eeedaad9..86422c88d 100644 --- a/portal/cookbook-guide.md +++ b/portal/cookbook-guide.md @@ -1,7 +1,13 @@ -# Cookbook Contributor's Guide +# Contributing a New Cookbook + +

    + + ← Back to Quickstart Guide + +

    Project Pythia Cookbooks are collections of more advanced and domain-specific example -workflows building on top of [Pythia Foundations](https://foundations.projectpythia.org/landing-page.html). +workflows building on top of [Pythia Foundations](https://foundations.projectpythia.org/). They are [geoscience](https://en.wikipedia.org/wiki/Earth_science)-focused and should direct the reader towards the Foundations material for any required background knowledge. @@ -10,7 +16,7 @@ The following is a step-by-step guide to creating a new Cookbook and getting it Before you begin, ask yourself if the content you are developing for a cookbook would be better suited as an addition to an existing cookbook. The best place to discuss cookbook ideas is the [Project Pythia category of the Pangeo Discourse](https://discourse.pangeo.io/c/education/project-pythia/60). -```{Note} +```{note} These instructions assume that your goal is to contribute a new Cookbook to the community-maintained collection housed on the [Pythia Cookbook Gallery](https://cookbooks.projectpythia.org). Using the Pythia Cookbook template to create reproducible documents housed elsewhere is definitely possible! But we don't focus on that use case in this guide. @@ -18,7 +24,7 @@ Using the Pythia Cookbook template to create reproducible documents housed elsew If you're not looking to create a _new_ Cookbook, but rather looking for guidance on contributing to _existing_ Cookbooks, first make sure you're comfortable with the [GitHub forking workflow](https://foundations.projectpythia.org/foundations/github/github-workflows.html#forking-workflow), then take a look at the section below on "Pull Requests and previews". -## Data access +## A. Data Access Before developing your cookbook, you should consider how it will access the data you plan to use. In loose order of preference, we recommend the following: @@ -27,62 +33,30 @@ Before developing your cookbook, you should consider how it will access the data 1. Discuss your larger data storage needs with the Pythia team. We are currently experimenting with cloud object storage for Cookbooks via NSF JetStream2. 1. Provide the tools and/or clear documentation for accessing the data that you have stored somewhere else -## Use the template +## B. Create a Repository From the Cookbook Template -1. If you don't already have a GitHub account, create one by following the [Getting Started with GitHub guide](https://foundations.projectpythia.org/foundations/getting-started-github.html) -1. On the [Cookbook Template repository](https://github.com/ProjectPythia/cookbook-template), click "Use this template → Create a new repository" +1. If you don't already have a GitHub account, create one by following the [Getting Started with GitHub guide](https://foundations.projectpythia.org/foundations/getting-started-github.html). +1. On the [Cookbook Template repository](https://github.com/ProjectPythia/cookbook-template), click "Use this template → Create a new repository". 1. Choose "Include all branches". -1. Give your repository a descriptive name followed by `-cookbook` (e.g., `hydrology-cookbook`, `hpc-cookbook`, `cesm-cookbook`) and a description -1. Create the repository. Your browser will be directed to the newly created repository under your GitHub account -1. Under Settings → Pages, ensure that GitHub Pages is enabled by checking that `Branch` is set to "gh-pages", and the folder set to "gh-pages/(root)". If it is not, change the Branch from "None" to "gh-pages/(root)" and click "Save" -1. Under Settings → Actions → General, make sure that "Read and write permissions" is selected. Screenshot 2023-01-13 at 3 12 47 PM +1. Give your repository a descriptive name followed by `-cookbook` (e.g., `hydrology-cookbook`, `hpc-cookbook`, `cesm-cookbook`) and a description. +1. Create the repository. Your browser will be directed to the newly created repository under your GitHub account. +1. Under Settings → Pages, ensure that GitHub Pages is enabled by checking that `Branch` is set to "gh-pages", and the folder set to "gh-pages/(root)". If it is not, change the Branch from "None" to "gh-pages/(root)" and click "Save". +1. Under Settings → Actions → General, make sure that "Read and write permissions" is selected. -Your cookbook is now ready to have content added! +Screenshot 2023-01-13 at 3 12 47 PM -```{Note} +```{note} In the rest of this guide, we assume that you are familiar with the basics of using git and GitHub. If not, we strongly recommend reading through our [GitHub tutorials in Foundations](https://foundations.projectpythia.org/foundations/getting-started-github.html). ``` -## Transfer your cookbook repo to the ProjectPythia organization - -In order for your Cookbook to be included in the Gallery, the source repository needs to be housed within the [Project Pythia GitHub organization](https://github.com/ProjectPythia). - -You can keep your repository in your personal GitHub space while you're developing your content if that works better for you. Repository ownership can be transferred at any time. - -However, we recommend transferring to the Pythia organization early, for a few reasons: -- Fewer settings to tweak later -- Easier to get help from the Pythia infrastructure team -- Encourages collaboration - -```{Note} -You're still in control! You will always retain write access to your Cookbook repository even after transferring ownership to the Pythia organization. - -Also, _don't worry about breaking anything!_ Your repo will not affect any other Project Pythia content until you initiate the request to list it on the [Cookbook Gallery](https://cookbooks.projectpythia.org) (see below...) -``` - -### Steps to transfer the repository - -1. [Contact the Pythia team via the Pangeo Discourse](https://discourse.pangeo.io/c/education/project-pythia/60) (or otherwise) to let us know about your cookbook. -1. You will get an invitation to join the [ProjectPythia organization](https://github.com/ProjectPythia). -1. Accept the GitHub invitation. _Welcome to the team!_ -1. Once you are a member of the organization, navigate to the Settings of your cookbook repository and scroll down to the **Danger Zone**. -1. Click "Transfer". -1. Select or type "ProjectPythia", confirm, and transfer. -1. When prompted about which teams to give access to, select "core". _This will enable the Pythia maintenance team to have full access to your repository._ - -Once you have successfully transferred the repository, you'll most likely want to make a [personal fork and a local clone of the repository](https://foundations.projectpythia.org/foundations/github/github-cloning-forking.html) so that you can continue to develop and collaborate on the Cookbook via the [forking workflow](https://foundations.projectpythia.org/foundations/github/github-workflows.html#forking-workflow). - -## Customize the paths in your repository - -Whether the repository lives in your personal GitHub space or on the ProjectPythia organization, there are several paths and links in the repository code that need to be updated to reflect the current home of your cookbook source. This step is necessary to ensure that the cookbook building and publishing infrastructure works as intended. +Your cookbook is now ready to have content added! -Fortunately this is quick and easy. Just run our custom GitHub action called `trigger-replace-links`: Navigate to "Actions" → "trigger-replace-links" → "Run workflow". -## Set up the computational environment +## C. Set up the Computational Environment You'll most likely want to do your edits in a [local clone of the repository](https://foundations.projectpythia.org/foundations/github/github-cloning-forking.html) on your laptop or wherever your are running your notebooks. -### Customizing your environment file +### Customizing Your Conda Environment 1. Within `environment.yml` (in the root of the repository), change `name` from `cookbook-dev` to `-dev` (e.g. `cesm-cookbook-dev`) and add all required libraries and other dependencies under `dependencies:`. Commit the changes. 1. Create the [conda environment](https://foundations.projectpythia.org/foundations/conda.html) with `conda env create -f environment.yml`. If it crashes, try running `conda config --set channel_priority strict` @@ -90,45 +64,47 @@ You'll most likely want to do your edits in a [local clone of the repository](ht You're now ready to create and run awesome notebooks. -### Customizing your GitHub actions - -Your repository includes automation for building and publishing your Cookbook, powered by [GitHub Actions](https://docs.github.com/en/actions). Now that you have created a custom name for your conda environment (`-dev`), you need to edit three files found in the `.github/workflows` section of your repo: -- `.github/workflows/nightly-build.yaml` -- `.github/workflows/publish-book.yaml` -- `.github/workflows/trigger-book-build.yaml` +## D. Develop your Cookbook -In each of these files, in the field called `environment_name:`, replace `cookbook-dev` with the name you used in your `environment.yml` file (probably `-dev`). Commit these changes. +### 1. Add Notebooks -```{Note} -If these workflow files look mysterious and you don't know anything about how GitHub Actions work, don't worry! The Pythia team will help with any problems that arise with the Cookbook automation. -``` +To add content, you should edit (and duplicate as necessary) the notebook template `notebooks/notebook-template.ipynb`. Using this template will help keep your content consistent with the expected Cookbook style. -## Develop your cookbook +### 2. Update Your Configuration File -### Add notebooks +The organization of your notebooks within the published book is dictated by the table of contents level of the `myst.yml` configuration file. _Edit this file to add your chapters / subfolders._ -To add content, you should edit (and duplicate as necessary) the notebook template `notebooks/notebook-template.ipynb`. Using this template will help keep your content consistent with the expected Cookbook style. +Required changes to your `myst.yml` file are all under the `project` section. Update the values for these fields: +- `title` +- `github` +- `authors` +- `toc` (table of contents) +- `Jupyter -> Binder -> Repo` -You can add subfolders to organize your notebook files as necessary. However, the organization of the notebooks within the published book is dictated by the table of contents file `_toc.yml`. _Edit this file to add your chapters._ Take a look at the [`radar-cookbook/_toc.yml`](https://github.com/ProjectPythia/radar-cookbook/blob/main/_toc.yml) for example syntax, or consult the [JupyterBook documentation](https://jupyterbook.org/en/stable/structure/toc.html). +Take a look at the [`radar-cookbook/myst.yml`](https://github.com/ProjectPythia/radar-cookbook/blob/main/_toc.yml) for example syntax. -### Customize your home page +### 3. Customize Your Home Page -The file `README.md` serves as the homepage of your Cookbook, so it should be descriptive. Edit this file as appropriate, following guidance in the file itself. +The file `README.md` serves as the homepage of your Cookbook, so it should be descriptive. Edit this file as appropriate, following guidance in the file itself. Make sure this file represents your new: +- Title +- Description +- Motivation +- Authors +- Content structure +- The paths in the nightly build, Binder, and DOI badges. -### Build your Cookbook locally +### 4. Build Your Cookbook Locally You should be able to build your new Cookbook on your local machine by running this command from the root of the repository: ``` -jupyter-book build . +myst start --execute ``` -from the root of your repository. This will execute all your notebooks and package them into a book styled with the Pythia theme. +from the root of your repository. This will execute all your notebooks. + +If the build is successful, you should be able to preview the result in a web browser at [localhost:3000](http://localhost:3000) -If the build is successful, you should be able to preview the result in a web browser by running -``` -open _build/html/index.html -``` -### Strip output from your notebooks before committing +### 5. Strip Output From Your Notebooks Before Committing Wherever feasible, we recommend only committing bare, unexecuted notebooks into your Cookbook repository. There are a few reasons: - The notebooks are executed automatically each time the book is built @@ -140,7 +116,67 @@ To strip a notebook, in a Jupyter Lab session, go to "Kernel" → "Restart Ke You're ready to push content up to GitHub and trigger the automated publishing pipeline. -## Deploying your Cookbook +## E. Making Your Cookbook Findable and Citable + +### 1. Credit yourself + +Cookbooks are scholarly objects. As part of the publication process, your Cookbook will get a citable DOI (via [Zenodo](https://zenodo.org)). Just as for journal publications, you need to make decisions about who gets credited for authorship. + +This information is managed manually through the file `CITATION.cff` in the root of your repository. This will determine the names displayed on your card in the [gallery](https://cookbooks.projectpythia.org/) as well as your DOI-based citation. + +Edit `CITATION.cff` as follows: + +- If you haven't already, _remove the people already listed in this file_. These are the credited authors of the [Cookbook Template](https://github.com/ProjectPythia/cookbook-template) only, and they should not be included in your author list (unless one of them also happens to be a content author for your book). +- Set your Cookbook title +- Write a short description / abstract (this will also appear on the [gallery](https://cookbooks.projectpythia.org/)) +- Include names of all content authors + - ORCID and other metadata for each author is optional but helpful +- Under the `name:` field, change "Cookbook Template contributors" to "[Your Cookbook Name] contributors" + +```{note} +GitHub automatically tracks all contributions to your repository. The folks who help with infrastructure fixes, content reviews, etc., are considered "contributors" but not primary authors. We include the "contributors" as a single group in `CITATION.cff` to acknowledge this! +``` + +### 2. Tag your Cookbook + +The file `_gallery_info.yml` determines how your Cookbook will be findable on the [gallery](https://cookbooks.projectpythia.org/). Edit this file with the following: + +- Replace `thumbnail.png` with your own image (which will appear on your gallery card) +- Edit the tags under `domains` and `packages` as appropriate. Check out the existing filters on the [gallery page](https://cookbooks.projectpythia.org/) to get a sense of how these are used. + + + +## F. Transfer Your Cookbook to the ProjectPythia Organization + +In order for your Cookbook to be included in the Gallery, the source repository needs to be housed within the [Project Pythia GitHub organization](https://github.com/ProjectPythia). + +You can keep your repository in your personal GitHub space while you're developing your content if that works better for you. Repository ownership can be transferred at any time. + +However, we recommend transferring to the Pythia organization early, for a few reasons: +- Fewer settings to tweak later +- Easier to get help from the Pythia infrastructure team +- Encourages collaboration + +```{note} +You're still in control! You will always retain write access to your Cookbook repository even after transfering ownership to the Pythia organization. + +Also, _don't worry about breaking anything!_ Your repo will not affect any other Project Pythia content until you initiate the request to list it on the [Cookbook Gallery](https://cookbooks.projectpythia.org) (see below...) +``` + +### Steps to Transfer the Repository + +1. [Contact the Pythia team via the Pangeo Discourse](https://discourse.pangeo.io/c/education/project-pythia/60) (or otherwise) to let us know about your cookbook. +1. You will get an invitation to join the [ProjectPythia organization](https://github.com/ProjectPythia). +1. Accept the GitHub invitation. _Welcome to the team!_ +1. Once you are a member of the organization, navigate to the Settings of your cookbook repository and scroll down to the **Danger Zone**. +1. Click "Transfer". +1. Select or type "ProjectPythia", confirm, and transfer. +1. When prompted about which teams to give access to, select "core". _This will enable the Pythia maintenance team to have full access to your repository._ + +Once you have successfully transferred the repository, you'll most likely want to make a [personal fork and a local clone of the repository](https://foundations.projectpythia.org/foundations/github/github-cloning-forking.html) so that you can continue to develop and collaborate on the Cookbook via the [forking workflow](https://foundations.projectpythia.org/foundations/github/github-workflows.html#forking-workflow). + + +## G. Deploying your Cookbook Pythia Cookbooks are not just collections of notebooks! They are backed by GitHub-based automation that handles building, testing, previewing, and publishing the books online. @@ -167,8 +203,8 @@ Here's a handy trick for finding your published book: The link to your published book will then be displayed on the home page of the repo. -```{Note} -If you have transferred your repository to the ProjectPythia organization and also made a personal fork, the publishing pipeline automation will _only run on the upstream fork on the ProjectPythia organization_ so there's only one copy of the "published" book. +```{note} +If you have transfered your repository to the ProjectPythia organization and also made a personal fork, the publishing pipeline automation will _only run on the upstream fork on the ProjectPythia organization_ so there's only one copy of the "published" book. It's possible to enable the workflows on your personal fork, but usually unnecessary if you preview your work via Pull Requests (see next section)! ``` @@ -187,8 +223,8 @@ Not satisfied? Keep making changes! Every new push to the feature branch on your ### Building on the Pythia Binder -```{Note} -By default, notebooks are executed on the free GitHub Actions service. This works fine for most lightweight Cookbooks. If your book is building and publishing successfully, you can safely ignore this section! +```{note} +By default, notebooks are executed on the free GitHub Actions service. This works fine for most lightweight Cookbooks. If your book is building and publishing succesfully, you can safely ignore this section! ``` For Cookbooks with substantial compute requirements, you have the option of routing notebook execution to a specialized Binder service maintained by the Project Pythia team. @@ -200,51 +236,13 @@ Here's how: - That will trigger a build and preview as usual, but the notebook execution will happen on the Binder. - If all is well, merge the changes, and all further builds will work this way. -```{Note} +```{note} The Binder uses your `environment.yml` file to create an image of an execution environment, which is stored for reuse. The time to execute your notebooks can vary, depending on whether the Binder needs to build a new image or not. ``` -## Publish your Cookbook on the Pythia Gallery - -Once you're happy with your content and your Cookbook is building and deploying properly, it's time to think about submitting it for inclusion in the [Cookbook Gallery](https://cookbooks.projectpythia.org/)! - -```{Note} -Cookbooks don't need to be "finished" in order to be accepted in the Gallery! Cookbooks are typically accepted so long as they run cleanly, are free of obvious errors, and have some relevant new content. - -Cookbooks are meant to be living documents. We encourage authors to open GitHub issues to track progress on adding and updating content. -``` - -At this stage, there are a few more steps to get things ready to go. - -### Authorship and the `CITATION.cff` file - -Cookbooks are scholarly objects. As part of the publication process, your Cookbook will get a citable DOI (via [Zenodo](https://zenodo.org)). Just as for journal publications, you need to make decisions about who gets credited for authorship. - -This information is managed manually through the file `CITATION.cff` in the root of your repository. This will determine the names displayed on your card in the [gallery](https://cookbooks.projectpythia.org/) as well as your DOI-based citation. - -Edit `CITATION.cff` as follows: - -- If you haven't already, _remove the people already listed in this file_. These are the credited authors of the [Cookbook Template](https://github.com/ProjectPythia/cookbook-template) only, and they should not be included in your author list (unless one of them also happens to be a content author for your book). -- Set your Cookbook title -- Write a short description / abstract (this will also appear on the [gallery](https://cookbooks.projectpythia.org/)) -- Include names of all content authors - - ORCID and other metadata for each author is optional but helpful -- Under the `name:` field, change "Cookbook Template contributors" to "[Your Cookbook Name] contributors" - -```{Note} -GitHub automatically tracks all contributions to your repository. The folks who help with infrastructure fixes, content reviews, etc., are considered "contributors" but not primary authors. We include the "contributors" as a single group in `CITATION.cff` to acknowledge this! -``` - -### Gallery tags +## H. Release a Version of Your Cookbook -The file `_gallery_info.yml` determines how your Cookbook will be findable on the [gallery](https://cookbooks.projectpythia.org/). Edit this file with the following: - -- Replace `thumbnail.png` with your own image (which will appear on your gallery card) -- Edit the tags under `domains` and `packages` as appropriate. Check out the existing filters on the [gallery page](https://cookbooks.projectpythia.org/) to get a sense of how these are used. - -### Generate a DOI - -Once all the above steps are complete and your `CITATION.cff` file is correct, you are ready to "release" a tagged version of your book and generate your first DOI. We've tried to make this as painless as possible, but there are a few more steps to take initially. +Once all the above steps are complete and your `CITATION.cff` file is correct, you are ready to "release" a tagged version of your book and generate your first DOI. We've tried to make this as painless as possible, but there are a few more steps to take initially. **For be able to initiate a release, you must have admin-level access to the Cookbook repository.** As always, reach out to a Pythia team member for help with any of these steps! @@ -256,24 +254,30 @@ As always, reach out to a Pythia team member for help with any of these steps! 1. Make a new release of your Cookbook repository! This is on the right nav side of the page from your code-view in the repository. 1. The DOI will now be generated automatically. The DOI badge on your homepage should link to the new archive that was just created on Zenodo. -### Initiate the Cookbook review process + +## I. Publish Your Cookbook on the Pythia Cookbook Gallery + +Once you're happy with your content and your Cookbook is building and deploying properly, it's time to think about submitting it for inclusion in the [Cookbook Gallery](https://cookbooks.projectpythia.org/)! + +```{note} +Cookbooks don't need to be "finished" in order to accepted in the Gallery! Cookbooks are typically accepted so long as they run cleanly, are free of obvious errors, and have some relevant new content. + +Cookbooks are meant to be living documents. We encourage authors to open GitHub issues to track progress on adding and updating content. +``` + +At this stage, there are a few more steps to get things ready to go. + + +### 1. Initiate the Cookbook review process If you haven't already, now is a great time to [contact the Project Pythia team](https://discourse.pangeo.io/c/education/project-pythia/60) to let them know about your new Cookbook. You will be assigned a Cookbook advocate from the Pythia maintenance team. They will open an issue on your Cookbook repository with the [Cookbook Checklist](cookbook-tasklist.md) for you to document your completion of the above process, plus a few more GitHub-specific steps. Once you complete this process, your Cookbook will be ready for review and publication! -### Submit your Cookbook to the Gallery - -Click the button below to request addition of your Cookbook to the [Project Pythia Cookbook Gallery](https://cookbooks.projectpythia.org). - - - - Submit a new Cookbook - - +### 2. Submit your Cookbook to the Gallery -1. Click the above button, or use this link to the [new cookbook PR template](https://github.com/ProjectPythia/cookbook-gallery/issues/new?assignees=ProjectPythia%2Feducation&labels=content%2Ccookbook-gallery-submission&template=update-cookbook-gallery.yaml&title=Update+Gallery+with+new+Cookbook). -1. Add the root name of your cookbook repository (e.g., just "cesm-cookbook", not the whole URL) and any other information you'd like the team to know. -1. The Pythia team will take a look at your content, and work with you on any necessary updates and fixes. +1. Following fork-branch workflows, append the [`cookbook-gallery/cookbook_gallery.txt`](https://github.com/ProjectPythia/cookbook-gallery/blob/main/cookbook_gallery.txt) file to include the root name of your repository (e.g., just "cesm-cookbook", not the whole URL). +2. Open a Pull Request and tag a Project Pythia team member for review. +3. The Pythia team will take a look at your content, and work with you on any necessary updates and fixes. __*THANK YOU FOR YOUR AMAZING CONTRIBUTION TO THIS COMMUNITY RESOURCE!*__ diff --git a/portal/index.md b/portal/index.md index e237b8358..67e99adae 100644 --- a/portal/index.md +++ b/portal/index.md @@ -1,62 +1,58 @@ -# Project Pythia +--- +site: + hide_outline: true + hide_toc: true + hide_title_block: true +--- -:::{banner} -:color: rgba(26, 100, 143, 0.9) -:image: _static/images/backgrounds/pexels-jeff-stapleton-5792818.jpg -:caption: Photo by Jeff Stapleton from Pexels -:class: dark-banner -::: ++++ {"kind": "split-image"} -An education and training hub for the geoscientific Python community
    +## Project Pythia -:::{admonition} **Service Notice** -:class: warning, large-banner -We are transitioning our infrastructure from Jupyter Book to Jupyter Book 2. **You may experience service interruptions on Tuesday, June 17th.** +:::{image} _static/images/backgrounds/pexels-jeff-stapleton-5792818.jpg +:alt: Photo by Jeff Stapleton from Pexels ::: - - Save the date for the 2025 Cook-off hackathon! - -
    +An education and training hub for the geoscientific Python community + +Project Pythia is hosting a Cookbook Cook-Off August 5-8, 2025. + +{button}`Cook-Off 2025` + +{button}`2024 Cookbooks<./posts/new-cookbooks.md>` - - Round-up of new Cookbooks from our 2024 Cook-off hackathon! - -
    +Donate to support Project Pythia! +{button}`Donate now` - - Donate to support Project Pythia! - -
    ++++ { "kind": "justified"} -[Project Pythia](about) is the education working group for [Pangeo](https://pangeo.io) +## What is Project Pythia? + +{button}`Learn More →<./about.md>` + +{button}`About Pangeo →` + +[Project Pythia](./about.md) is the education working group for [Pangeo](https://pangeo.io) and is an educational resource for the entire geoscience community. Together these initiatives are helping geoscientists make sense of huge volumes of numerical scientific data using tools that facilitate open, reproducible science, and building a [community of practice](https://en.wikipedia.org/wiki/Community_of_practice) around these goals. -Project Pythia is a home for Python-centered learning resources that are _open-source_, +Project Pythia is a home for Python-justified learning resources that are _open-source_, _community-owned_, _geoscience-focused_, and _high-quality_. - - - About Project Pythia - -
    - - About Pangeo logo - Pangeo - -
    - - +

    ## Start Learning Project Pythia has several resources for you to use to start learning how to use Python and the technology in the Python ecosystem for the geosciences. -### The Foundations Book +::::{grid} 1 2 2 3 +:::{card} +:url: https://foundations.projectpythia.org +:header: The Foundations Book +:footer: Visit the Foundations Book » The [Pythia Foundations Book](https://foundations.projectpythia.org) is a [Jupyter Book](https://jupyterbook.org/) developed by the Pythia community to @@ -64,144 +60,50 @@ act as a **comprehensive set of tutorials** covering the **foundational skills** everyone needs to get started with **computing in the open-source Python ecosystem**. These foundational tutorials will serve as common references for more advanced and domain-specific content to be housed here in the Pythia Portal. +::: - - - Read the Pythia Foundations Book - - - -### Pythia Cookbooks +:::{card} +:url: https://cookbooks.projectpythia.org +:header: Pythia Cookbooks +:footer: Visit Pythia Cookbooks » Pythia Cookbooks are collections of more advanced and domain-specific example workflows building on top of Pythia Foundations. +::: - - - Visit Pythia Cookbooks - - - -### The Resource Gallery +:::{card} +:url: https://projectpythia.org/resource-gallery/ +:header: The Resource Gallery +:footer: Visit the Resource Gallery » There is a wealth of educational resources out there on the internet for learning Python and how to use it in the geosciences! We have attempted to gather together a curated and filterable list of these resources (including Pythia's own content) -into our [Pythia Resource Gallery](/resource-gallery). +into our [Pythia Resource Gallery](https://projectpythia.org/resource-gallery/). Click the link below to see a hand-picked selection of resources for learning at your own pace. +::: +:::: - - - Visit the Pythia Resource Gallery - - - -### Webinar Tutorial Series - -The [Pythia Webinar Tutorial Series](https://ncar-xdev.github.io/status/tutorials.html) is hosted regularly by the Project Pythia team. Anyone can join -these hour-long, instructor-led interactive tutorials. Participants can -sit back and learn, or download a Jupyter Notebook and work along with -the instructor. Each seminar is recorded for later viewing on the -[Project Pythia YouTube channel](https://www.youtube.com/channel/UCoZPBqJal5uKpO8ZiwzavCw). -Click the link below to view past and upcoming seminars, covering a range of -topics from beginning to advanced. - - - - Visit the Pythia Webinar Tutorial Series - - - -### Pythia Data Collection - -Looking for a free, public domain geoscience data set that you can use to -sharpen your skills while learning the Scientific Python Ecosystem? All of -the example data used by the [Pythia Foundations Book](https://foundations.projectpythia.org) are freely accessible. - - - - Visit the Pythia Data Repository - - - - - +

    ## Join us! +{button}`Pangeo Discourse →` + If you have questions or want to share anything with the Project Pythia Team, please reach out to us through the [Project Pythia category on the Pangeo Discourse forum](https://discourse.pangeo.io/c/education/project-pythia/) or join us at any of our [Monthly Project Meetings](#monthly-pythia-meetings). - - - Go to Pangeo Discourse - - - ### Contributing Anyone can contribute to and participate in Project Pythia! We conduct all of our work in the open, and all of our work is Open Source Licensed. We welcome contributions from anyone in the community. Please see our [Contributor’s Guide](/contributing) -for details on how you can get involved, and come see our work in the +for details on how you can get involved, our [Code of Conduct](https://github.com/projectpythia/projectpythia.github.io/blob/main/CODEOFCONDUCT.md) for an overview of contribution rules and responsibilities, and come see our work in the [ProjectPythia GitHub Organization](https://github.com/ProjectPythia). - +(monthly-pythia-meetings)= ### Monthly Pythia Meetings @@ -215,12 +117,11 @@ We publish all notes and agendas of our [Community Meetings](https://docs.google.com/document/d/e/2PACX-1vQWQrgHs_G5XyNH5GTFYydH_woUZcyZibdxPUWLpqFUYs20WM93kdx5onwOaizC_3-tfnbreMNQbYAp/pub) and [Outreach Meetings](https://docs.google.com/document/d/e/2PACX-1vQBAt5B24wig2eh-hxHzgJiXjKCpSeGKsw3PFizZjwH7ka71dagipKwCwQvmE-obmSOfR4Psj2lgbvU/pub). -### Meeting & Event Calendar +:::{iframe} https://calendar.google.com/calendar/embed?src=c_4qpvf316afd9mv0ci7d2uiafog%40group.calendar.google.com +::: -
    - -
    +

    ## How to Cite The material in Project Pythia is licensed for free and open consumption and reuse. All code is served under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0), while all non-code content is licensed under [Creative Commons BY 4.0 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/). Effectively, this means you are free to share and adapt this material so long as you give appropriate credit to the Project Pythia community. @@ -230,19 +131,3 @@ To cite this project, please site our [NSF GEO OSE proposal](https://zenodo.org/ > Rose, Brian E. J., Clyne, John, May, Ryan, Munroe, James, Snyder, Amelia, Eroglu, Orhan, & Tyle, Kevin. (2023). Collaborative Research: GEO OSE TRACK 2: Project Pythia and Pangeo. Zenodo. https://doi.org/10.5281/zenodo.8184298 [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.8184298.svg)](https://doi.org/10.5281/zenodo.8184298) - - -```{toctree} ---- -hidden: True -maxdepth: 1 ---- -about.md -contributing.md -cookbook-guide.md -cookbook-tasklist.md -code_of_conduct.md -resource-gallery.md -blog.md -metrics.md -``` diff --git a/portal/make.bat b/portal/make.bat deleted file mode 100644 index 2119f5109..000000000 --- a/portal/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/portal/metrics.md b/portal/metrics.md index b05321b8f..051e7823d 100644 --- a/portal/metrics.md +++ b/portal/metrics.md @@ -1 +1,373 @@ +--- +kernelspec: + name: python3 + display_name: Python 3 +--- + # Metrics + +```{code-cell} python3 +:tags: [remove-cell] + +import datetime +import json +import os + +import cartopy +import cartopy.io +import google +import matplotlib +import matplotlib.cm as cm +import matplotlib.colors as colors +import matplotlib.pyplot as plt +import numpy as np +import warnings + +from google.analytics.data_v1beta import BetaAnalyticsDataClient +from google.analytics.data_v1beta.types import DateRange, Dimension, Metric, RunReportRequest + +# Project ID Numbers +PORTAL_ID = '266784902' +FOUNDATIONS_ID = '281776420' +COOKBOOKS_ID = '324070631' + +# Access Secrets +PRIVATE_KEY_ID = os.environ.get('PRIVATE_KEY_ID') +# Ensure GH secrets doesn't introduce extra '\' new line characters (related to '\' being an escape character) +PRIVATE_KEY = os.environ.get('PRIVATE_KEY').replace('\\n', '\n') + +credentials_dict = { + 'type': 'service_account', + 'project_id': 'cisl-vast-pythia', + 'private_key_id': PRIVATE_KEY_ID, + 'private_key': PRIVATE_KEY, + 'client_email': 'pythia-metrics-api@cisl-vast-pythia.iam.gserviceaccount.com', + 'client_id': '113402578114110723940', + 'auth_uri': 'https://accounts.google.com/o/oauth2/auth', + 'token_uri': 'https://oauth2.googleapis.com/token', + 'auth_provider_x509_cert_url': 'https://www.googleapis.com/oauth2/v1/certs', + 'client_x509_cert_url': 'https://www.googleapis.com/robot/v1/metadata/x509/pythia-metrics-api%40cisl-vast-pythia.iam.gserviceaccount.com', + 'universe_domain': 'googleapis.com', +} + +try: + client = BetaAnalyticsDataClient.from_service_account_info(credentials_dict) +except google.auth.exceptions.MalformedError as e: + print('Malformed Error:', repr(e)) + # Insight into reason for failure without exposing secret key + # 0: Secret not found, else malformed + # 706: extra quote, 732: extra '\', 734: both + print('Length of PRIVATE_KEY:', len(PRIVATE_KEY)) + +pre_project_date = '2020-03-31' +``` + + +Last Updated: {eval}`str(datetime.datetime.now())` + +This metrics page provides an overview of user activity collected by Google Analytics across the three pillars of Project Pythia: our portal which includes information about the project as well as our resource gallery, our Foundations book, and our Cookbooks gallery. Information is either all-time (from a pre-project start date of March 2020) or year-to-date as indicated and is updated nightly to provide real-time and automated insights into our engagement, impact, and audience reach. If you would like to request a different metrics analysis, timeframe, or view, please [open a GitHub issue](https://github.com/ProjectPythia/projectpythia.github.io/issues/new/choose). + +## Table of Total Active Users by Project + +```{code-cell} python3 +:tags: [remove-cell] + +def _format_rounding(value): + """ + Helper function for rounding string displays. 1,232 -> 1.2K + """ + return f'{round(value / 1000, 1):.1f}K' + + +# The rest of this file alternates between functions for requesting information from Google Analytics +# And functions that use that request image to form either a .json or a .png file to be used in write-metrics-md.py +def _run_total_users_report(property_id): + """ + Function for requesting cumulative active users from a project since project start. + """ + request = RunReportRequest( + property=f'properties/{property_id}', + dimensions=[], + metrics=[Metric(name='activeUsers')], + date_ranges=[DateRange(start_date=pre_project_date, end_date='today')], + ) + response = client.run_report(request) + + total_users = 0 + for row in response.rows: + total_users += int(row.metric_values[0].value) + + return _format_rounding(total_users) +``` + +This table displays the total active users of our 3 Pythia projects over the life of Project Pythia. Google analytics defines active users as the number of unique people who have visited the site and met certain [engagement requirements](https://support.google.com/analytics/answer/9234069?sjid=8697784525616937194-NC). You can read more from the [GA4 "Understand User Metrics" documentation](https://support.google.com/analytics/answer/12253918?hl=en). + +```{code-cell} python3 +:tags: [remove-cell] + +portal_users = _run_total_users_report(PORTAL_ID) +foundations_users = _run_total_users_report(FOUNDATIONS_ID) +cookbooks_users = _run_total_users_report(COOKBOOKS_ID) +``` + +(table-total-users)= +| Project | All-Time Users | +| ----------- | ------------------------- | +| Portal | {eval}`portal_users` | +| Foundations | {eval}`foundations_users` | +| Cookbooks | {eval}`cookbooks_users` | + +## Chart of Active Users by Project Since Year Start + +```{code-cell} python3 +:tags: [remove-cell] + +def _run_active_users_this_year(property_id): + """ + Function for requesting active users by day from a project since year start. + """ + current_year = datetime.datetime.now().year + start_date = f'{current_year}-01-01' + + request = RunReportRequest( + property=f'properties/{property_id}', + dimensions=[Dimension(name='date')], + metrics=[Metric(name='activeUsers')], + date_ranges=[DateRange(start_date=start_date, end_date='today')], + ) + response = client.run_report(request) + + dates = [] + user_counts = [] + for row in response.rows: + date_str = row.dimension_values[0].value + date = datetime.datetime.strptime(date_str, '%Y%m%d') + dates.append(date) + user_counts.append(int(row.metric_values[0].value)) + + # Days need to be sorted chronologically + return zip(*sorted(zip(dates, user_counts), key=lambda x: x[0])) + + +def plot_projects_this_year(PORTAL_ID, FOUNDATIONS_ID, COOKBOOKS_ID): + """ + Function for taking year-to-date active users by day and plotting it for each project. + """ + portal_dates, portal_users = _run_active_users_this_year(PORTAL_ID) + foundations_dates, foundations_users = _run_active_users_this_year(FOUNDATIONS_ID) + cookbooks_dates, cookbooks_users = _run_active_users_this_year(COOKBOOKS_ID) + + # Plotting code + plt.figure(figsize=(10, 5.5)) + plt.title('Year-to-Date Pythia Active Users', fontsize=15) + + plt.plot(portal_dates, portal_users, color='purple', label='Portal') + plt.plot(foundations_dates, foundations_users, color='royalblue', label='Foundations') + plt.plot(cookbooks_dates, cookbooks_users, color='indianred', label='Cookbooks') + + plt.legend(fontsize=12, loc='upper right') + + plt.xlabel('Date', fontsize=12) + plt.show() + +``` + +This line plot displays active users for our 3 Pythia projects (Portal in purple, Foundations in blue, and Cookbooks in salmon) since January 1st of the current year. + +```{code-cell} python3 +:tags: [remove-input] +:name: plot-active-users +:caption: Chart of active users by project since year start. + +plot_projects_this_year(PORTAL_ID, FOUNDATIONS_ID, COOKBOOKS_ID) +``` + +## Chart of Top 5 Pages by Project + +```{code-cell} python3 +:tags: [remove-cell] + +def _run_top_pages_report(property_id): + """ + Function for requesting top 5 pages from a project. + """ + request = RunReportRequest( + property=f'properties/{property_id}', + dimensions=[Dimension(name='pageTitle')], + date_ranges=[DateRange(start_date=pre_project_date, end_date='today')], + metrics=[Metric(name='screenPageViews')], + ) + response = client.run_report(request) + + views_dict = {} + for row in response.rows: + page = row.dimension_values[0].value + views = int(row.metric_values[0].value) + views_dict[page] = views + + # Sort by views and grab the top 5 + top_pages = sorted(views_dict.items(), key=lambda item: item[1], reverse=True)[:5] + # String manipulation on page titles "Cartopy - Pythia Foundations" -> "Cartopy" + pages = [page.split('—')[0] for page, _ in top_pages] + views = [views for _, views in top_pages] + + # Reverse order of lists, so they'll plot with most visited page on top (i.e. last) + return pages[::-1], views[::-1] +def plot_top_pages(PORTAL_ID, FOUNDATIONS_ID, COOKBOOKS_ID): + """ + Function that takes the top 5 viewed pages for all 3 projects and plot them on a histogram. + """ + portal_pages, portal_views = _run_top_pages_report(PORTAL_ID) + foundations_pages, foundations_views = _run_top_pages_report(FOUNDATIONS_ID) + cookbooks_pages, cookbooks_views = _run_top_pages_report(COOKBOOKS_ID) + + # Plotting code + fig, ax = plt.subplots(figsize=(10, 5.5)) + plt.title('All-Time Top Pages', fontsize=15) + + y = np.arange(5) # 0-4 for Cookbooks + y2 = np.arange(6, 11) # 6-10 for Foundations + y3 = np.arange(12, 17) # 12-16 for Portal + + bar1 = ax.barh(y3, portal_views, align='center', label='Portal', color='purple') + bar2 = ax.barh(y2, foundations_views, align='center', label='Foundations', color='royalblue') + bar3 = ax.barh(y, cookbooks_views, align='center', label='Cookbooks', color='indianred') + + y4 = np.append(y, y2) + y4 = np.append(y4, y3) # 0-4,6-19,12-6 for page labels to have a gap between projects + pages = cookbooks_pages + foundations_pages + portal_pages # List of all pages + ax.set_yticks(y4, labels=pages, fontsize=12) + + # Adds round-formatted views label to end of each bar + ax.bar_label(bar1, fmt=_format_rounding, padding=5, fontsize=10) + ax.bar_label(bar2, fmt=_format_rounding, padding=5, fontsize=10) + ax.bar_label(bar3, fmt=_format_rounding, padding=5, fontsize=10) + + ax.set_xscale('log') + ax.set_xlim([10, 10**5]) # set_xlim must be after setting xscale to log + ax.set_xlabel('Page Views', fontsize=12) + + plt.legend(fontsize=12, loc='lower right') + plt.show() +``` + +This bar-chart displays the top 5 pages by project over the life of Project Pythia, as determined by screen page views. Screen page views refers to the number of times users viewed a page, including repeated visits. To learn more visit the [GA4 "API Dimensions & Metrics" page](https://developers.google.com/analytics/devguides/reporting/data/v1/api-schema). + +```{code-cell} python3 +:tags: [remove-input] +:name: chart-top-five-pages +:caption: Bar chart of the top five pages by project over the life of Project Pythia + +plot_top_pages(PORTAL_ID, FOUNDATIONS_ID, COOKBOOKS_ID) +``` + +## Map of Total Foundation Active Users by Country + +```{code-cell} python3 +:tags: [remove-cell] + +# Disable cartopy warning +warnings.filterwarnings('ignore', category=cartopy.io.DownloadWarning) + +def _run_usersXcountry_report(property_id): + """ + Function for requesting users by country for a project. + """ + request = RunReportRequest( + property=f'properties/{property_id}', + dimensions=[Dimension(name='country')], + metrics=[Metric(name='activeUsers')], + date_ranges=[DateRange(start_date=pre_project_date, end_date='today')], + ) + response = client.run_report(request) + + user_by_country = {} + for row in response.rows: + country = row.dimension_values[0].value + users = int(row.metric_values[0].value) + user_by_country[country] = user_by_country.get(country, 0) + users + + return user_by_country +def plot_usersXcountry(FOUNDATIONS_ID): + """ + Function for taking users by country for Pythia Foundations and plotting them on a map. + """ + users_by_country = _run_usersXcountry_report(FOUNDATIONS_ID) + + # Google API Country names do not match Cartopy Country Shapefile names + dict_api2cartopy = { + 'Tanzania': 'United Republic of Tanzania', + 'United States': 'United States of America', + 'Congo - Kinshasa': 'Democratic Republic of the Congo', + 'Bahamas': 'The Bahamas', + 'Timor-Leste': 'East Timor', + 'C\u00f4te d\u2019Ivoire': 'Ivory Coast', + 'Bosnia & Herzegovina': 'Bosnia and Herzegovina', + 'Serbia': 'Republic of Serbia', + 'Trinidad & Tobago': 'Trinidad and Tobago', + } + + for key in dict_api2cartopy: + users_by_country[dict_api2cartopy[key]] = users_by_country.pop(key) + + # Sort by views and grab the top 10 countries for a text box + top_10_countries = sorted(users_by_country.items(), key=lambda item: item[1], reverse=True)[:10] + top_10_text = '\n'.join( + f'{country}: {_format_rounding(value)}' for i, (country, value) in enumerate(top_10_countries) + ) + + # Plotting code + fig = plt.figure(figsize=(10, 4)) + ax = plt.axes(projection=cartopy.crs.PlateCarree(), frameon=False) + ax.set_title('All-Time Pythia Foundations Users by Country', fontsize=15) + + shapefile = cartopy.io.shapereader.natural_earth(category='cultural', resolution='110m', name='admin_0_countries') + reader = cartopy.io.shapereader.Reader(shapefile) + countries = reader.records() + + colormap = plt.get_cmap('Blues') + newcmp = colors.ListedColormap(colormap(np.linspace(0.2, 1, 128))) # Truncate colormap to remove white hues + newcmp.set_extremes(under='grey') + + norm = colors.LogNorm(vmin=1, vmax=max(users_by_country.values())) # Plot on log scale + mappable = cm.ScalarMappable(norm=norm, cmap=newcmp) + + # Loop through countries and plot their color + for country in countries: + country_name = country.attributes['SOVEREIGNT'] + if country_name in users_by_country.keys(): + facecolor = newcmp(norm(users_by_country[country_name])) + ax.add_geometries( + [country.geometry], + cartopy.crs.PlateCarree(), + facecolor=facecolor, + edgecolor='white', + linewidth=0.7, + norm=matplotlib.colors.LogNorm(), + ) + else: + ax.add_geometries( + [country.geometry], cartopy.crs.PlateCarree(), facecolor='grey', edgecolor='white', linewidth=0.7 + ) + + # Add colorbar + cax = fig.add_axes([0.05, -0.015, 0.7, 0.03]) # [x0, y0, width, height] + cbar = fig.colorbar(mappable=mappable, cax=cax, spacing='uniform', orientation='horizontal', extend='min') + cbar.set_label('Unique Users') + + # Add top 10 countries text + props = dict(boxstyle='round', facecolor='white', edgecolor='white') + ax.text(1.01, 0.5, top_10_text, transform=ax.transAxes, fontsize=12, verticalalignment='center', bbox=props) + + plt.show() +``` + +This map displays the number of active users per country for Pythia Foundations for the entire life of Project Pythia. + +```{code-cell} python3 +:tags: [remove-input] +:name: map-active-users-country +:caption: Map of the number of active users per country for Pythia Foundations for the entire life of Project Pythia. + +plot_usersXcountry(FOUNDATIONS_ID) +``` diff --git a/portal/metrics/user_metrics.json b/portal/metrics/user_metrics.json deleted file mode 100644 index 57a123c7f..000000000 --- a/portal/metrics/user_metrics.json +++ /dev/null @@ -1 +0,0 @@ -{"Now": "", "Portal": "0K", "Foundations": "0K", "Cookbooks": "0K"} diff --git a/portal/myst.yml b/portal/myst.yml new file mode 100644 index 000000000..7e1a285f6 --- /dev/null +++ b/portal/myst.yml @@ -0,0 +1,60 @@ +# See docs at: https://mystmd.org/guide/frontmatter +version: 1 +extends: + - https://raw.githubusercontent.com/projectpythia/pythia-config/main/pythia.yml +project: + id: 770e49e5-344a-4c46-adaa-3afb060b2085 + authors: Project Pythia Community + github: https://github.com/projectpythia/projectpythia.github.io + + toc: + - file: index.md + - file: about.md + - title: Blog + children: + # - pattern: posts/*.md + # Temporary until we have blog infrastructure: explicit list of posts by date (newest first) + - file: posts/cookoff2025-website.md + - file: posts/binderhub_status.md + - file: posts/new-cookbooks.md + - file: posts/cookoff2024-website.md + - file: posts/cookoff2024-savethedate.md + - file: posts/fundraiser.md + - file: posts/cookoff2023.md + - file: contributing.md + - file: cookbook-guide.md + - file: quick-cookbook-guide.md + - file: metrics.md +site: + domains: [] + options: + style: style.css + parts: + footer: | + :::::{grid} 3 + :class: items-center + + + ```{image} https://raw.githubusercontent.com/ProjectPythia/pythia-config/main/logos/NSF-NCAR.png + :alt: NCAR Logo + ``` + + ```{image} https://raw.githubusercontent.com/ProjectPythia/pythia-config/main/logos/NSF-Unidata.png + :alt: Unidata Logo + ``` + + ```{image} https://raw.githubusercontent.com/ProjectPythia/pythia-config/main/logos/UAlbany.svg + :alt: UAlbany Logo + ``` + ::::: + + :::::{div} + :class: flex items-center gap-4 text-disclaimer + + ```{image} https://raw.githubusercontent.com/ProjectPythia/pythia-config/main/logos/nsf.jpg + :alt: NSF Logo + :height: 60px + ``` + + This material is based upon work supported by the National Science Foundation under Grant Nos. 2026863 and 2026899. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation. + ::::: diff --git a/portal/posts/binderhub_status.md b/portal/posts/binderhub_status.md index 0bdc87bae..4ef3acecb 100644 --- a/portal/posts/binderhub_status.md +++ b/portal/posts/binderhub_status.md @@ -1,7 +1,6 @@ --- -blogpost: true -date: Jan 20, 2025 -author: Kevin Tyle +date: 2025-01-20 +author: ktyle tags: ["binderhub", "maintenance"] --- diff --git a/portal/posts/cookoff2023.md b/portal/posts/cookoff2023.md index 025c11566..d2b365ae2 100644 --- a/portal/posts/cookoff2023.md +++ b/portal/posts/cookoff2023.md @@ -1,8 +1,7 @@ --- -blogpost: true -date: Jun 28, 2023 -author: Julia kent -tags: cook-off +date: 2023-06-28 +author: jukent +tags: [cook-off] --- # Pythia Cookbook Cook-Off Hackathon 2023 diff --git a/portal/posts/cookoff2024-savethedate.md b/portal/posts/cookoff2024-savethedate.md index aa52a628a..459858440 100644 --- a/portal/posts/cookoff2024-savethedate.md +++ b/portal/posts/cookoff2024-savethedate.md @@ -1,8 +1,7 @@ --- -blogpost: true -date: Aug 29, 2023 -author: John Clyne -tags: cook-off +date: 2023-08-29 +author: clyne +tags: [cook-off] --- # Pythia Cookbook Cook-Off Hackathon 2024 - Save the Date! diff --git a/portal/posts/cookoff2024-website.md b/portal/posts/cookoff2024-website.md index 975051b02..8274fc004 100644 --- a/portal/posts/cookoff2024-website.md +++ b/portal/posts/cookoff2024-website.md @@ -1,8 +1,7 @@ --- -blogpost: true -date: Jan 8, 2024 -author: Brian Rose -tags: cook-off +date: 2024-01-08 +author: brian-rose +tags: [cook-off] --- # Website is live for the 2024 Cook-off hackathon diff --git a/portal/posts/cookoff2025-website.md b/portal/posts/cookoff2025-website.md index ee9a282de..85384886b 100644 --- a/portal/posts/cookoff2025-website.md +++ b/portal/posts/cookoff2025-website.md @@ -1,8 +1,7 @@ --- -blogpost: true -date: Jun 03, 2025 -author: Kevin Tyle -tags: cook-off +date: 2025-02-20 +author: jukent +tags: [cook-off] --- # Register now for the 2025 Cook-off hackathon! diff --git a/portal/posts/fundraiser.md b/portal/posts/fundraiser.md index bc199b2b2..d78a74b74 100644 --- a/portal/posts/fundraiser.md +++ b/portal/posts/fundraiser.md @@ -1,18 +1,19 @@ --- -blogpost: true -date: Jun 28, 2023 -author: Julia kent -tags: fundraiser +date: 2023-06-28 +author: jukent +tags: [fundraiser] --- # Donate to Support Project Pythia! ## You can make an impact on our community! + By donating to support Project Pythia you are investing in an important educational resource for the entire geoscience community, from students to late career. Project Pythia is an education working group helping geoscientists make sense of huge volumes of numerical scientific data using tools that facilitate open, reproducible science, and building a community of practice around these goals. Project Pythia is a home for Python-centered learning resources that are open-source, community-owned, geoscience-focused, and high-quality. Donations contribute to outreach and community engagement activities, such as participant support at our annual hackathons. ## Friends of the National Center + Friends of the National Center is the fundraising arm of the University Corporation for Atmospheric Research (UCAR), which manages the National Science Foundation National Center for Atmospheric Research. UCAR is a non-profit organization, so all donations to Project Pythia are tax-deductible to the fullest extent allowed by law. We accept donations through the following ways:
    diff --git a/portal/posts/new-cookbooks.md b/portal/posts/new-cookbooks.md index b1234c2d6..4afd329b9 100644 --- a/portal/posts/new-cookbooks.md +++ b/portal/posts/new-cookbooks.md @@ -1,8 +1,7 @@ --- -blogpost: true -date: Jan 10, 2025 -author: Brian Rose -tags: cook-off +date: 2025-01-10 +author: brian-rose +tags: [cook-off] --- # Hello world, new Cookbooks! @@ -11,52 +10,61 @@ The Project Pythia team is excited to announce the publication of new community- [Cookbooks](https://cookbooks.projectpythia.org) are geoscience-flavored collections of tutorials, recipes, and reproducible example workflows for Python-based data analysis and visualization. Cookbooks are supported by a rich computational infrastructure enabling collaborative authoring, automated health-checking, and interactive use. Cookbooks undergo a review process, are citable by DOI, and are tested and maintained by the Project Pythia community. -It's time to celebrate the Cook-off projects that have made it across the finish line. *[Watch this space!](https://cookbooks.projectpythia.org)* There are several more exciting Cookbooks still in development. +It's time to celebrate the Cook-off projects that have made it across the finish line. _[Watch this space!](https://cookbooks.projectpythia.org)_ There are several more exciting Cookbooks still in development. ## Cookbook round-up Here you can find brief descriptions and links to the new (and significantly updated) books on the [Pythia Cookbook Gallery](https://cookbooks.projectpythia.org). Try also filtering the [gallery](https://cookbooks.projectpythia.org) with the new "events" dropdown menu! - ### [EOFs Cookbook](https://projectpythia.org/eofs-cookbook/) -EOF cookbook thumbnail +```{image} https://raw.githubusercontent.com/ProjectPythia/eofs-cookbook/main/notebooks/images/nh-slp-eofs.png +:alt: EOF cookbook thumbnail +:width: 600 +:align: left +``` **Authors:** [Robert Ford](https://github.com/r-ford) -[![DOI](https://zenodo.org/badge/656765685.svg)](https://zenodo.org/badge/latestdoi/656765685) + This Cookbook covers Empirical Orthogonal Function (EOF) analysis and its application to climate data. EOF analysis is an essential tool for studying the variability of the atmosphere-ocean system. Meteorological and oceanographic data is noisy and multidimensional, but an EOF analysis allows us to pull out patterns from the data that might otherwise be difficult to find. The goal of this cookbook is to provide background and context to the analysis alongside practical examples of carrying out the analysis using Python packages. **Contributors:** - + - ### [Ocean Biogeochemistry Cookbook](https://projectpythia.org/ocean-bgc-cookbook/) - +```{image} https://raw.githubusercontent.com/ProjectPythia/ocean-bgc-cookbook/main/coccolithophore_kristen_krumhardt.png +:alt: Ocean Biogeochemistry Cookbook thumbnail +:width: 300 +:align: left +``` **Authors:** [Lev Romashkov](https://github.com/rmshkv) and [Kristen Krumhardt](https://github.com/kristenkrumhardt) -DOI + This Cookbook covers working with various sources of ocean biogeochemistry data, including Community Earth System Model (CESM) output and observational data. It provides a brief introduction to some metrics important to ocean biogeochemistry, from physical quantities like temperature to biological quantities like plankton biomass. It also demonstrates some of the data science techniques used to work with this information, and provides an introduction to the relationship between modeled and observational estimates. **Contributors:** - + - ### [Investigating interhemispheric precipitation changes over the past millennium](https://projectpythia.org/paleoPCA-cookbook/) -LinkedEarth Logo +```{image} https://raw.githubusercontent.com/LinkedEarth/Logos/master/LinkedEarth/LinkedEarth_medium.png +:alt: LinkedEarth Logo +:width: 400 +:align: left +``` **Authors:** [Deborah Khider](https://github.com/khider), [Hari Sundar](https://github.com/sriharisundar), and [Varun Ratnakar](https://github.com/varunratnakar) -[![DOI](https://zenodo.org/badge/813352705.svg)](https://zenodo.org/badge/latestdoi/813352705) + This Cookbook covers paleoclimate model-data comparison using spatio-temporal pattern obtained using Principal Component Analysis (PCA). @@ -64,20 +72,22 @@ Paleoclimate observations obtained from tree rings or the geochemical compositio Recently, [Steinman et al. (2022)](https://doi.org/10.1073/pnas.2120015119) used PCA analysis to compare model and data output. Here, we use a similar approach with the [CESM Last Millennium simulation](https://www.cesm.ucar.edu/community-projects/lme) and proxy records stored on the [LiPDverse](https://lipdverse.org). This repository contains paleoclimate datasets that have been curated by the community and are archived in a standard format, facilitating analysis. - **Contributors:** - + - ### [ESGF Cookbook](https://projectpythia.org/esgf-cookbook/) -thumbnail +```{image} https://raw.githubusercontent.com/ProjectPythia/esgf-cookbook/refs/heads/main/notebooks/images/logos/esgf2-us.png?raw=true +:alt: ESGF2 thumbnail +:width: 250 +:align: left +``` **Authors:** [Max Grover](@mgrover1), [Nathan Collier](@nocollier), [Carsten Ehbrecht](@cehbrecht), [Jacqueline Nugent](@jacnugent), and [Gerardo Rivera Tello](@griverat) -[![DOI](https://zenodo.org/badge/721319801.svg)](https://doi.org/10.5281/zenodo.11663067) + This Cookbook covers how to access and analyze datasets that can be accessed from Earth System Grid Federation (ESGF) cyberinfrastructure. @@ -85,16 +95,20 @@ This cookbook focuses on highlighting analysis recipes, as well as data acccess **Contributors:** - + ### [Radar Cookbook](https://projectpythia.org/radar-cookbook/) -radar thumbnail +```{image} https://raw.githubusercontent.com/projectpythia/radar-cookbook/main/notebooks/images/logos/arm_logo.png +:alt: ARM logo +:width: 300 +:align: left +``` **Authors:** [Max Grover](https://github.com/mgrover1), [Zachary Sherman](https://github.com/zssherman), [Milind Sharma](https://github.com/gewitterblitz), [Alfonso Ladino](https://github.com/aladinor), [Crystal Camron](https://github.com/crystalclearwx), and [Takashi Unuma](https://github.com/TakashiUNUMA) -[![DOI](https://zenodo.org/badge/479066261.svg)](https://zenodo.org/badge/latestdoi/479066261) + Pythia's very first cookbook continues to grow! @@ -104,18 +118,19 @@ The Example Workflows section contains a growing collection of real-world case s **Contributors:** - + - ### [ARCO ERA-5 Interactive Visualization Cookbook](https://projectpythia.org/ERA5_interactive-cookbook/) -ERA5 thumbnail +```{image} https://raw.githubusercontent.com/ProjectPythia/ERA5_interactive-cookbook/main/thumbnail.png +:alt: ERA5 thumbnail +:width: 300 +``` **Authors:** [Kevin Tyle](https://github.com/ktyle), [Michael Barletta](https://github.com/Michael-Barletta), [Negin Sobhani](https://github.com/negin513), [Nicholas Cote](https://github.com/ncote) , [Harsha Hampapura](https://github.com/hrhampapura) , and [Philip Chmielowiec](https://github.com/philip2c) -[![DOI](https://zenodo.org/badge/657280462.svg)](https://zenodo.org/badge/latestdoi/657280462) + This Cookbook covers accessing, regridding, and visualizing the [ECMWF Reanalysis version 5](https://www.ecmwf.int/en/forecasts/dataset/ecmwf-reanalysis-v5) (aka **ERA-5**) dataset in a [Analysis Ready, Cloud Optimized](https://www.frontiersin.org/articles/10.3389/fclim.2021.782909/full) (aka **ARCO**) format. @@ -130,29 +145,30 @@ This Cookbook demonstrates how to do the following: 3. Plot a map at a specific time using Matplotlib and Cartopy 4. Create interactive visualizations, leveraging the [Holoviz](https://holoviz.org) ecosystem - **Contributors:** - + ### [Wavelet Cookbook](https://projectpythia.org/wavelet-cookbook/) -Wavelet thumbnail +```{image} https://raw.githubusercontent.com/ProjectPythia/wavelet-cookbook/main/thumbnail.png +:alt: Wavelet thumbnail +:width: 300 +:align: left +``` **Authors:** [Cora Schneck](https://github.com/cyschneck) -[![DOI](https://zenodo.org/badge/815311051.svg)](https://zenodo.org/badge/latestdoi/815311051) + This Cookbook covers how to work with wavelets in Python. Wavelets are a powerful tool to analyze time-series data. When data frequencies vary over time, wavelets can be applied to analysis trends and overcome the time/frequency limitations of Fourier Transforms **Contributors:** - + - ## The 2024 Cook-off ### In pictures @@ -168,14 +184,14 @@ This Cookbook covers how to work with wavelets in Python. Wavelets are a powerfu - Number of attendees: 92 (49 in-person, 43 remote) - Number of attendees receiving travel support from Project Pythia: 16 - Career stage: - - 14% undergrad - - 29% graduate - - 14% postdoc - - 43% faculty or staff + - 14% undergrad + - 29% graduate + - 14% postdoc + - 43% faculty or staff - Gender of attendees: - - 59% men - - 30% women - - 11% other + - 59% men + - 30% women + - 11% other - Number of individual project breakout groups: 9 - New cookbooks published to the [Pythia Gallery](https://cookbooks.projectpythia.org): 5 _(as of this writing)_ - Heavily revised cookbooks: 2 diff --git a/portal/quick-cookbook-guide.md b/portal/quick-cookbook-guide.md new file mode 100644 index 000000000..3401f43c3 --- /dev/null +++ b/portal/quick-cookbook-guide.md @@ -0,0 +1,64 @@ +# Quick Guide to Contributing a New Cookbook + +This document is designed be lightweight. For more detailed, step-by-step instuctions, see the **Full Guide for Cookbook Guide** +

    + + Contributing a New Cookbook → + +

    + + +--- +## Create Repository Using Template + +1. Navigate to [projectpythia/cookbook-template](https://github.com/projectpythia/cookbook-template) +2. Click **"Use This Template"** → **"Create a new repository"** + +### On the next page: +- Check **"Include all branches"** +- You are the **owner** +- Create your repository name (should end with `-cookbook`) +- Select to keep it **Public** +- Then click **"Create repository"** + + +--- + +## Update Content + +Follow [Git/GitHub best practices](https://foundations.projectpythia.org/foundations/getting-started-github.html) with your collaborators: + +- Add content with your scientific insight as `.ipynb` files in the `notebooks/` folder +- Update `environment.yml` to include all necessary packages +- Update your **thumbnail** to visually represent your work +- Update `README.md` to reflect: + - Title + - Description + - Motivation + - Authors + - Content structure + +- Update `myst.yml` with: + - Title + - GitHub link + - Table of contents (`toc`) + - Jupyter → Binder → Repo links + +- Update `CITATION.cff` with: + - Authors’ names, ORCID, website, and affiliation + - Title + - Abstract + +- Update `_gallery_info.yml` with: + - "packages" and "domains" tags + - the correct filepath for your thumbnail + +--- + +## Ready to Publish? + +1. Make sure you’re added to the `ProjectPythia` organization +2. From **repository settings**, transfer the repo to the `ProjectPythia` organization +3. Open a PR editing `cookbook_gallery.txt` with your repo name in the [Cookbook Gallery Repository](https://github.com/projectpythia/cookbook-gallery) +4. Await review +5. Someone from the Pythia team will publish a **citable release** diff --git a/portal/resource_gallery.yaml b/portal/resource_gallery.yaml deleted file mode 100644 index 70fefa7f2..000000000 --- a/portal/resource_gallery.yaml +++ /dev/null @@ -1,1571 +0,0 @@ -- title: Your First Python Tutorial - url: https://ncar.github.io/python-tutorial/index.html - description: | - A tutorial for getting started with Python aimed at scientists with experience in at least one other coding language. Designed to teach you Python, not package specific syntax. - authors: - - name: Xdev Team - email: xdev@ucar.edu - url: https://ncar.github.io/xdev/ - affiliation: NSF NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/Xdev.png - tags: - packages: - - pure python - formats: - - tutorial - affiliation: - - pythia - - xdev - -- title: Unidata Python Training - url: https://unidata.github.io/python-training/python/intro-to-python/ - description: | - Introduction to Python for Atmospheric Science and Meteorology. Unidata is working to create a collection of online training materials focused on the use of Python in the atmospheric sciences. While our examples and scenarios may feature Unidata tools and data technologies, our aim is to present a generic set of freely available tools that are generally useful to scientists, educators, and students in the geosciences, broadly defined. - authors: - - affiliation: Unidata - affiliation_url: https://www.unidata.ucar.edu/ - thumbnail: /_static/thumbnails/unidata_150x150.png - tags: - formats: - - course - affiliation: - - unidata - -- title: Unidata Python Workshop - Jupyter Notebooks Introduction - url: - https://unidata.github.io/python-training/workshop/Jupyter_Notebooks/jupyter-notebooks-introduction/ - description: | - Jupyter notebooks are a great way to have code, output, images, video, and other information in one place. Notebooks are an ideal tool for the student, research scientist, and even software developer. In this lesson we will go over the basic features of Jupyter notebooks and how to use them. - authors: - - affiliation: Unidata - affiliation_url: https://www.unidata.ucar.edu/ - thumbnail: /_static/thumbnails/unidata_150x150.png - tags: - packages: - - jupyter - affiliation: - - unidata - -- title: An Introduction to Earth and Environmental Data Science - url: https://earth-env-data-science.github.io/intro - description: | - This book grew out of a course developed at Columbia University called Research Computing in Earth Science. It was written mostly by Ryan Abernathey, with significant contributions from Kerry Key. By separating the book from the class, we hope to create an open-source community resource for python education in the Earth and Environmental Sciences. - authors: - - name: Ryan Abernathey - affiliation: Lamont-Doherty Earth Observatory - - name: Kerry Key - affiliation: Lamont-Doherty Earth Observatory - thumbnail: /_static/thumbnails/earth_env_data_science.png - tags: - formats: - - course - affiliation: - - pangeo - -- title: The Climate Laboratory - url: https://brian-rose.github.io/ClimateLaboratoryBook/home - description: | - A hands on approach to climate physics and climate modeling. Fundamental climate processes are introduced through interactive and reproducible Python-based modeling exercises. This JupyterBook serves as a course textbook at the University of Albany. - authors: - - name: Brian Rose - url: http://www.atmos.albany.edu/facstaff/brose/ - affiliation: University of Albany - thumbnail: /_static/thumbnails/climlablogo.png - tags: - formats: - - course - - book - domains: - - climate science - - atmospheric science - packages: - - climlab - -- title: Earth Analytics Python Course - url: https://www.earthdatascience.org/courses/earth-analytics-python/ - description: | - Earth analytics is an intermediate, multidisciplinary course that addresses major questions in Earth science and teaches students to use the analytical tools necessary to undertake exploration of heterogeneous ‘big scientific data’. This course is designed for graduate students and students participating in the Earth Data Analytics Professional Certificate. - authors: - - name: Earth Lab - url: https://www.earthdatascience.org/ - affiliation: University of Colorado Boulder - thumbnail: /_static/thumbnails/earth-lab-logo.png - tags: - formats: - - course - -- title: Earth Data Science - url: https://www.earthdatascience.org/ - description: | - This site contains open, tutorials and course materials covering topics including data integration, GIS and data intensive science. - authors: - - name: Earth Lab - affiliation: University of Colorado Boulder - thumbnail: /_static/thumbnails/earth-lab-logo.png - tags: - formats: - - course - -- title: Geohackweek 2019 - url: https://geohackweek.github.io/ - description: | - Geohackweek is a 5-day hackweek to be held at the University of Washington eScience Institute. Tutorials from the hackweek are available for everyone to follow (participants and non-participants alike). - authors: - - name: eScience Institute - url: https://escience.washington.edu/ - affiliation: University of Washington - thumbnail: /_static/thumbnails/eScience_Logo_HR.png - tags: - formats: - - course - -- title: Research Software Engineering with Python - url: https://merely-useful.github.io/py-rse/ - description: | - We believe every researcher should know how to write short programs that clean and analyze data in a reproducible way and how to use version control to keep track of what they have done. But just as some astronomers spend their careers designing telescopes, some researchers focus on building the software that makes research possible. People who do this are called research software engineers; the aim of this book is to get you ready for this role by helping you go from writing code for yourself to creating tools that help your entire field advance. - authors: - - name: Damien Irving, et al. - thumbnail: - tags: - formats: - - course - -- title: Data Analysis with Python- Zero to Pandas - url: https://jovian.ai/learn/data-analysis-with-python-zero-to-pandas - description: | - "Data Analysis with Python: Zero to Pandas" is a practical and beginner-friendly introduction to data analysis covering the basics of Python, Numpy, Pandas, Data Visualization, and Exploratory Data Analysis. - authors: - - name: Jovian.ai - url: https://www.jovian.ai/ - thumbnail: /_static/thumbnails/zero-to-pandas.png - tags: - formats: - - course - -- title: MTH271- Mathematical Computing with Data - url: http://web.pdx.edu/~gjay/teaching/mth271_2020/html/_CONTENTS.html - description: | - In this course, we will have multiple occasions to procure, analyze, and visualize data. We will study mathematical and statistical techniques to discern patterns in complex data. We shall do so in an ecosystem of python computing modules developed by open-source enthusiasts worldwide. - authors: - - name: Jay Gopalakrishnan - url: http://web.pdx.edu/~gjay/ - affiliation: Portland State University - thumbnail: /_static/thumbnails/portland-state.png - tags: - formats: - - course - -- title: ATSC301- Atmospheric Radiation and Remote Sensing - url: https://clouds.eos.ubc.ca/~phil/courses/atsc301/ - description: | - This course teaches radiation and remote sensing, but also covers: how to write clear, documented and tested code that can ingest, manipulate and display data, and how to turn equations into computer algorithms in Python. - authors: - - name: Philip Austin - email: paustin@eos.ubc.ca - affiliation: University of British Columbia - thumbnail: /_static/thumbnails/british-columbia.jpg - tags: - formats: - - course - domains: - - remote sensing - -- title: Introduction to Geospatial Concepts - url: https://datacarpentry.org/organization-geospatial/ - description: | - The goal of this lesson is to provide an introduction to core geospatial data concepts. It is intended for learners who have no prior experience working with geospatial data, and as a pre-requisite for the R for Raster and Vector Data lesson . This lesson can be taught in approximately 75 minutes and covers the following topics: Introduction to raster and vector data format and attributes, examples of data types commonly stored in raster vs vector format, an introduction to categorical vs continuous raster data and multi-layer rasters, an introduction to the file types and R packages used in the remainder of this workshop, an introduction to coordinate reference systems and the PROJ4 format, and an overview of commonly used programs and applications for working with geospatial data. - authors: - - name: The Carpentries - url: https://carpentries.org/ - thumbnail: /_static/thumbnails/DC_logo_vision.png - tags: - formats: - - course - -- title: Dani Arribas-Bel Course Repositories - url: http://darribas.org/materials.html - description: | - A collection of courework repositories for various data science classes taught by Dani Arribas-Bel. - authors: - - name: Dani Arribas-Bel - affiliation: University of Liverpool Department of Geography and Planning - affiliation_url: https://www.liverpool.ac.uk/geography-and-planning/ - thumbnail: - tags: - formats: - - course - domains: - - data science - -- title: Cartopy Documentation - url: https://scitools.org.uk/cartopy/docs/latest/ - description: | - Cartopy is a Python package designed for geospatial data processing in order to produce maps and other geospatial data analyses. - authors: - - name: UK Met Office - thumbnail: /_static/thumbnails/cartopy.png - tags: - packages: - - cartopy - formats: - - documentation - -- title: Cartopy Gallery - url: https://scitools.org.uk/cartopy/docs/latest/gallery/index.html - description: | - Visual examples demonstrating some of the functionality of Cartopy, particularly its matplotlib interface. - authors: - - name: UK Met Office - thumbnail: /_static/thumbnails/cartopy.png - tags: - packages: - - cartopy - formats: - - gallery - domains: - - data visualization - -- title: Contextily Documentation - url: https://contextily.readthedocs.io - description: | - contextily is a small Python 3 (3.6 and above) package to retrieve tile maps from the internet. It can add those tiles as basemap to matplotlib figures or write tile maps to disk into geospatial raster files. Bounding boxes can be passed in both WGS84 (EPSG:4326) and Spheric Mercator (EPSG:3857). - authors: - - name: Dani Arribas-Bel & Contexily Contributors - thumbnail: - tags: - packages: - - contextily - formats: - - documentation - - gallery - -- title: Contextily Tutorial - url: https://contextily.readthedocs.io/en/latest/intro_guide.html - description: | - Welcome to the taster guide for contextily, the package for contextual tiles in Python. In this notebook, we will show the basic functionality available in contextily, a package to work with web-tiles for background maps. To do that, we will use additional data to illustrate contextily can be integrated with other libraries such as geopandas and rasterio. - authors: - - name: Dani Arribas-Bel & Contexily Contributors - thumbnail: - tags: - packages: - - contextily - formats: - - tutorial - -- title: Dask - url: https://dask.org - description: | - Dask provides advanced parallelism for analytics, enabling performance at scale for the tools you love. - authors: - - name: Dask core developers - thumbnail: /_static/thumbnails/dask.png - tags: - packages: - - dask - formats: - - documentation - -- title: Dask Tutorial - url: https://tutorial.dask.org/ - description: | - Dask is a parallel computing library that scales the existing Python ecosystem. This tutorial will introduce Dask and parallel data analysis more generally. - authors: - - name: Dask Developers - thumbnail: /_static/thumbnails/dask.png - tags: - packages: - - dask - formats: - - tutorial - -- title: GeoCAT Tutorial - url: https://geocat-comp.readthedocs.io/en/latest/examples.html - description: | - The examples below show GeoCAT-comp functions being utilized in real-world use cases. They also demonstrate how GeoCAT-comp can be used to make plots with Matplotlib (using Cartopy) and PyNGL (work in progress). - authors: - - name: GeoCat - url: https://geocat.ucar.edu/ - affiliation: NSF NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/geocat.png - tags: - packages: - - geocat-comp - formats: - - tutorial - -- title: GeoCAT Gallery - url: https://geocat-examples.readthedocs.io/ - description: | - A gallery of plotting examples from GeoCat. - authors: - - name: GeoCAT - url: https://geocat.ucar.edu/ - affiliation: NSF NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/geocat.png - tags: - packages: - - geocat-comp - - geocat-viz - formats: - - gallery - domains: - - data visualization - -- title: GeoPandas Documentation - url: https://geopandas.org - description: | - GeoPandas is an open source project to make working with geospatial data in python easier. GeoPandas extends the datatypes used by pandas to allow spatial operations on geometric types. Geometric operations are performed by shapely. Geopandas further depends on fiona for file access and matplotlib for plotting. - authors: - - name: GeoPandas developers - thumbnail: /_static/thumbnails/geopandas_icon.png - tags: - packages: - - geopandas - formats: - - documentation - -- title: GeoPandas Gallery - url: https://geopandas.org/gallery/index.html - description: | - Examples that show off the functionality in GeoPandas. They highlight many of the things you can do with this package, and show off some best-practices. - authors: - - name: GeoPandas developers - thumbnail: /_static/thumbnails/geopandas_icon.png - tags: - packages: - - geopandas - formats: - - gallery - -- title: hvPlot - url: https://hvplot.holoviz.org/ - description: | - hvPlot provides a high-level plotting API built on HoloViews that provides a general and consistent API for plotting data in all the abovementioned formats. hvPlot can integrate neatly with the individual libraries if an extension mechanism for the native plot APIs is offered, or it can be used as a standalone component. - authors: - - name: HoloViz developers - url: https://holoviz.org/ - thumbnail: /_static/thumbnails/hvplot-wm.png - tags: - packages: - - hvPlot - formats: - - documentation - -- title: hvPlot Tutorial - url: https://hvplot.holoviz.org/user_guide/index.html - description: | - The user guide provides a detailed introduction to the API and features of hvPlot. In the Introduction you will learn how to activate the plotting API and start using it. Next you will learn to use the API for tabular data and get an overview of the types of plots you can generate and how to customize them; including how to customize interactivity using widgets. Next is an overview on how to display and save plots in the notebook, on the commandline, and from a script. Another section will introduce you to generating subplots from your data. Once the basics are covered you can learn how to use the plotting API for specific types of data including streaming data, gridded data network graphs, geographic data, and timeseries data. - authors: - - name: HoloViz developers - url: https://holoviz.org/ - thumbnail: /_static/thumbnails/hvplot-wm.png - tags: - packages: - - hvPlot - formats: - - tutorial - domains: - - data visualization - -- title: hvPlot Gallery - url: https://hvplot.holoviz.org/reference/index.html - description: | - Incomplete Reference Gallery containing some small examples of different plot types using hvPlot. - authors: - - name: HoloViz developers - url: https://holoviz.org/ - thumbnail: /_static/thumbnails/hvplot-wm.png - tags: - packages: - - hvPlot - formats: - - gallery - domains: - - data visualization - -- title: Ipyleaflet - url: https://ipyleaflet.readthedocs.io - description: | - Interactive maps in the Jupyter notebook. - authors: - - name: The Jupyter development team - thumbnail: - tags: - packages: - - ipyleaflet - formats: - - documentation - -- title: Matplotlib - url: https://matplotlib.org/ - description: | - Matplotlib is a comprehensive library for creating static, animated, and interactive visualizations in Python. - authors: - - name: the Matplotlib development team - thumbnail: /_static/thumbnails/matplotlib.svg - tags: - packages: - - matplotlib - formats: - - documentation - domains: - - data visualization - -- title: Matplotlib Tutorial - url: https://matplotlib.org/stable/tutorials/index.html - description: | - This page contains more in-depth guides for using Matplotlib. It is broken up into beginner, intermediate, and advanced sections, as well as sections covering specific topics. - authors: - - name: the Matplotlib development team - thumbnail: /_static/thumbnails/matplotlib.svg - tags: - packages: - - matplotlib - formats: - - tutorial - domains: - - data visualization - -- title: Matplotlib Gallery - url: https://matplotlib.org/stable/gallery/index.html - description: | - This gallery contains examples of the many things you can do with Matplotlib. - authors: - - name: the Matplotlib development team - thumbnail: /_static/thumbnails/matplotlib.svg - tags: - packages: - - matplotlib - formats: - - gallery - domains: - - data visualization - -- title: MetPy - url: https://unidata.github.io/MetPy/latest/index.html - description: | - MetPy is a collection of tools in Python for reading, visualizing, and performing calculations with weather data. - authors: - - name: MetPy developers - affiliation: NSF Unidata - affiliation_url: https://www.unidata.ucar.edu/ - thumbnail: /_static/thumbnails/metpy.jpg - tags: - packages: - - metpy - formats: - - documentation - -- title: MetPy Tutorial - url: https://unidata.github.io/MetPy/latest/tutorials/index.html - description: | - This collection of tutorials (under development) demonstrates the use of MetPy to perform common meteorological tasks. - authors: - - name: MetPy developers - affiliation: NSF Unidata - affiliation_url: https://www.unidata.ucar.edu/ - thumbnail: /_static/thumbnails/metpy.jpg - tags: - packages: - - metpy - formats: - - tutorial - -- title: Unidata Python Workshop - MetPy - url: - https://unidata.github.io/python-training/workshop/Metpy_Introduction/introduction-to-metpy/ - description: | - MetPy is a modern meteorological open-source toolkit for Python. It is a maintained project of Unidata to serve the academic meteorological community. MetPy consists of three major areas of functionality: plots, calculations, and file i/o. - authors: - - affiliation: Unidata - affiliation_url: https://www.unidata.ucar.edu/ - thumbnail: /_static/thumbnails/unidata_150x150.png - tags: - packages: - - metpy - formats: - - tutorial - -- title: MetPy Gallery - url: https://unidata.github.io/MetPy/latest/examples/index.html - description: | - Examples of using a variety of MetPy’s functionality together. - authors: - - name: MetPy Developers - affiliation: NSF Unidata - affiliation_url: https://www.unidata.ucar.edu/ - thumbnail: /_static/thumbnails/metpy.jpg - tags: - packages: - - metpy - formats: - - gallery - -- title: MetPy Mondays - url: https://www.youtube.com/playlist?list=PLQut5OXpV-0ir4IdllSt1iEZKTwFBa7kO - description: | - A weekly video series showing how to use MetPy or other Python libraries to solve problems relevant to geoscience applications. - authors: - - affiliation: NSF Unidata - affiliation_url: https://www.unidata.ucar.edu/ - thumbnail: /_static/thumbnails/metpy-mondays.jpeg - tags: - packages: - - metpy - formats: - - video - -- title: Numpy - url: https://numpy.org - description: | - The fundamental package for scientific computing with Python. - authors: - - name: NumPy - thumbnail: /_static/thumbnails/numpy.png - tags: - packages: - - numpy - formats: - - documentation - -- title: Numpy Tutorials - url: https://numpy.org/learn/ - description: | - A curated collection of external resources teaching numpy from beginner to advanced uses. - authors: - - name: NumPy - thumbnail: /_static/thumbnails/numpy.png - tags: - packages: - - numpy - -- title: Intro to Numerical Computing with NumPy - url: https://youtu.be/ZB7BZMhfPgk - description: | - NumPy provides Python with a powerful array processing library and an elegant syntax that is well suited to expressing computational algorithms clearly and efficiently. We'll introduce basic array syntax and array indexing, review some of the available mathematical functions in NumPy, and discuss how to write your own routines. Along the way, we'll learn just enough about matplotlib to display results from our examples. - authors: - - name: A. Chabot-LeClerc - affiliation: Enthought - affiliation_url: https://www.enthought.com/ - thumbnail: /_static/thumbnails/Enthought.png - tags: - packages: - - numpy - formats: - - video - -- title: Pandas - url: https://pandas.pydata.org/ - description: | - Pandas is a fast, powerful, flexible and easy to use open source data analysis and manipulation tool, built on top of the Python programming language. - authors: - - name: Pandas Development Team - thumbnail: /_static/thumbnails/pandas.png - tags: - packages: - - pandas - formats: - - documentation - -- title: Pandas Tutorials - url: https://pandas.pydata.org/docs/getting_started/intro_tutorials/index.html - description: | - Tutorial for getting started with pandas. - authors: - - name: Pandas Development Team - thumbnail: /_static/thumbnails/pandas.png - tags: - packages: - - pandas - formats: - - tutorial - -- title: Pandas Gallery - url: https://pandas.pydata.org/pandas-docs/stable/user_guide/visualization.html - description: | - A gallery of example pandas functionality. - authors: - - name: Pandas Development Team - thumbnail: /_static/thumbnails/pandas.png - tags: - packages: - - pandas - formats: - - gallery - -- title: Rasterio - url: https://rasterio.readthedocs.io - description: | - Geographic information systems use GeoTIFF and other formats to organize and store gridded raster datasets such as satellite imagery and terrain models. Rasterio reads and writes these formats and provides a Python API based on Numpy N-dimensional arrays and GeoJSON. - authors: - - name: MapBox - thumbnail: - tags: - packages: - - rasterio - formats: - - documentation - -- title: Rasterio Tutorial - url: https://rasterio.readthedocs.io/en/latest/quickstart.html - description: | - This document explains how to use Rasterio to read existing files and to create new files. Some advanced topics are glossed over to be covered in more detail elsewhere in Rasterio’s documentation. - authors: - - name: MapBox - thumbnail: /_static/thumbnails/rasterio-tutorial.png - tags: - packages: - - rasterio - formats: - - tutorial - -- title: Siphon - url: https://unidata.github.io/siphon - description: | - Siphon is a collection of Python utilities for downloading data from remote data services. Much of Siphon’s current functionality focuses on access to data hosted on a THREDDS Data Server. It also provides clients to a variety of simple web services. - authors: - - name: Siphon Contributors - affiliation: NSF Unidata - affiliation_url: https://www.unidata.ucar.edu/ - thumbnail: /_static/thumbnails/siphon.png - tags: - packages: - - siphon - formats: - - documentation - -- title: Siphon Gallery - url: https://unidata.github.io/siphon/latest/examples/index.html - description: | - Examples of using Siphon’s functionality. - authors: - - name: Siphon Contributors - affiliation: NSF Unidata - affiliation_url: https://www.unidata.ucar.edu/ - thumbnail: /_static/thumbnails/siphon.png - tags: - packages: - - siphon - formats: - - gallery - -- title: WRF-Python - url: https://wrf-python.readthedocs.io - description: | - A collection of diagnostic and interpolation routines for use with output from the Weather Research and Forecasting (WRF-ARW) Model. - authors: - - affiliation: NSF NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: - tags: - packages: - - wrf-python - formats: - - documentation - -- title: WRF-Python Gallery - url: https://wrf-python.readthedocs.io/en/latest/plot.html - description: | - Examples of how wrf-python can be used to make plots with matplotlib (with basemap and cartopy) and PyNGL. None of these examples make use of xarray’s builtin plotting functions, since additional work is most likely needed to extend xarray in order to work correctly. - authors: - - affiliation: NSF NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: - tags: - packages: - - wrf-python - formats: - - gallery - -- title: Xarray - url: http://xarray.pydata.org/ - description: | - xarray (formerly xray) is an open source project and Python package that makes working with labelled multi-dimensional arrays simple, efficient, and fun! - - Xarray introduces labels in the form of dimensions, coordinates and attributes on top of raw NumPy-like arrays, which allows for a more intuitive, more concise, and less error-prone developer experience. The package includes a large and growing library of domain-agnostic functions for advanced analytics and visualization with these data structures. - - Xarray is inspired by and borrows heavily from pandas, the popular data analysis package focused on labelled tabular data. It is particularly tailored to working with netCDF files, which were the source of xarray’s data model, and integrates tightly with dask for parallel computing. - authors: - - name: Xarray Developers - thumbnail: /_static/thumbnails/xarray.png - tags: - packages: - - xarray - formats: - - documentation - -- title: Xarray Tutorial - url: https://tutorial.xarray.dev/intro.html - description: | - A collection of tutorials provided by the Xarray developers. - authors: - - name: Xarray Developers - thumbnail: /_static/thumbnails/xarray.png - tags: - packages: - - xarray - - dask - formats: - - tutorial - -- title: Python Programming for Earth Science Students - description: Python Programming for Earth Science Students - url: - https://nbviewer.jupyter.org/github/ltauxe/Python-for-Earth-Science-Students/tree/master/ - thumbnail: '' - authors: - - name: Lisa Tauxe - affiliation: Scripps Institution of Oceanography - affiliation_url: https://scripps.ucsd.edu/ - email: ltauxe@ucsd.edu - - name: Hanna Asefaw - affiliation: '' - affiliation_url: '' - email: hasefaw@ucsd.edu - - name: Brendan Cych - affiliation: '' - affiliation_url: '' - email: bcych@ucsd.edu - tags: - formats: - - tutorial - packages: - - pure python - - pandas - - scipy - - numpy - - matplotlib - -- title: Pangeo Gallery - description: A gallery of different Pangeo use cases - url: http://gallery.pangeo.io - thumbnail: - https://github.com/pangeo-data/pangeo/raw/master/docs/_static/pangeo_simple_logo.png - authors: - - name: Pangeo Community - affiliation: Pangeo - affiliation_url: http://pangeo.io/ - email: '' - tags: - packages: - - jupyter - - xarray - - dask - - matplotlib - - intake - - xgcm - - holoviews - - pandas - - numpy - formats: - - gallery - - tutorial - domains: - - glaciology - - remote sensing - - physical oceanography - - hydrology - affiliation: - - pangeo - -- title: Python for Atmosphere and Ocean Science - description: Lesson materials for a one-day workshop on using Python in the atmosphere - and ocean sciences. Useful for any geoscientist working with raster (a.k.a. “gridded”) - data, the lessons cover packages/tools including conda, xarray, dask and netCDF, - as well as best practices including functions, command line programs, defensive - programming, provenance tracking and version control via git/GitHub. - url: https://carpentrieslab.github.io/python-aos-lesson/ - thumbnail: /_static/thumbnails/DC_logo_vision.png - authors: - - name: '' - affiliation: Data Carpentry - affiliation_url: https://datacarpentry.org/ - email: '' - - name: '' - affiliation: '' - affiliation_url: '' - email: '' - tags: - packages: - - xarray - - dask - - cartopy - - numpy - - matplotlib - formats: - - course - domains: - - atmospheric science - - climate science - -- title: Python for Geosciences - description: 'This is a short overview of how Python is used in science and particularly - in geosciences targeting people who already know programming (e.g. Matlab). ' - url: https://github.com/koldunovn/python_for_geosciences - thumbnail: '' - authors: - - name: Nikolay Koldunov - affiliation: 'Alfred Wegener Institute for Polar and Marine Research ' - affiliation_url: https://www.awi.de/en/ - email: koldunovn@gmail.com - tags: - packages: - - matplotlib - - scipy - - pandas - - xarray - - cartopy - formats: - - course - - tutorial - - video - domains: - - atmospheric science - - climate science - - physical oceanography - - data science - -- title: Environmental Data Science Book - description: A living, open and community-driven resource to showcase and support - computational notebooks for collaborative, reproducible and transparent Environmental - Science - url: https://github.com/alan-turing-institute/environmental-ds-book - thumbnail: - https://raw.githubusercontent.com/alan-turing-institute/environmental-ds-book/master/book/figures/logo/logo.png - authors: - - name: EDS book community - affiliation: Environmental Data Science Book - affiliation_url: https://github.com/alan-turing-institute/environmental-ds-book#contributors - email: environmental.ds.book@gmail.com - tags: - packages: - - pangeo - - pooch - - intake - - pystac - - pandas - - geopandas - - xarray - - dask - - matplotlib - - holoviews - - cartopy - formats: - - book - - gallery - domains: - - environmental science - - climate science - - ecology - - hydrology - - physical geography - - remote sensing - - data science - affiliation: - - The Alan Turing Institute - -- title: Your First Python Tutorial - Reading in a .txt File - url: https://youtu.be/Jog7ybd6amw - description: | - Recording from the Python Tutorial Seminar Series designed to teach you Python, not package specific syntax. This lessons covers setting up a work environment and opening a .txt file. The content to follow along with this video is hosted on the Xdev Python Tutorial website. - authors: - - name: Project Pythia - - name: Julia Kent - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-txtfile.jpeg - tags: - packages: - - pure python - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Your First Python Tutorial - Creating a Data Dictionary - url: https://youtu.be/5z6-t62x7Xs - description: | - Recording from the Python Tutorial Seminar Series designed to teach you Python, not package specific syntax. This lessons covers creating a data dictionary. The content to follow along with this video is hosted on the Xdev Python Tutorial website. - authors: - - name: Project Pythia - - name: Julia Kent - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-datadict.jpeg - tags: - packages: - - pure python - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Your First Python Tutorial - Writing Functions - url: https://youtu.be/BerEf_3CsL8 - description: | - Recording from the Python Tutorial Seminar Series designed to teach you Python, not package specific syntax. This lessons covers how to write and call functions in Python. The content to follow along with this video is hosted on the Xdev Python Tutorial website. - authors: - - name: Project Pythia - - name: Julia Kent - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-writingfx.jpeg - tags: - packages: - - pure python - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Your First Python Tutorial - Creating Your Own Package - url: https://youtu.be/6lbbTwGFcTc - description: | - Recording from the Python Tutorial Seminar Series designed to teach you Python, not package specific syntax. This lessons covers how to create and call modules and packages. The content to follow along with this video is hosted on the Xdev Python Tutorial website. - authors: - - name: Project Pythia - - name: Julia Kent - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-firstpackage.jpeg - tags: - packages: - - pure python - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Your First Python Tutorial - Using a Built-In Package and Publishing Your - Package - url: https://youtu.be/44QUMCh2ZHU - description: | - Recording from the Python Tutorial Seminar Series designed to teach you Python, not package specific syntax. This lessons covers how to use your first external buil-in package, `math`, and how to publish your package. The content to follow along with this video is hosted on the Xdev Python Tutorial website. - authors: - - name: Project Pythia - - name: Julia Kent - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-builtinpackage.jpeg - tags: - packages: - - pure python - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Jupyter Notebooks - url: https://youtu.be/xSzXvwzFsDU - description: | - Recording from the Python Tutorial Seminar Series introducing JupyterLab and Jupyter Notebooks. - authors: - - name: Project Pythia - - name: Kevin Paul - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-jupyter.jpeg - tags: - packages: - - jupyter - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Numpy - url: https://youtu.be/kstc-6uz7AQ - description: | - Recording from the Python Tutorial Seminar Series introducing the Python Package `numpy`. The content to follow along with this video is hosted on this Numpy Google Collab. - authors: - - name: Project Pythia - - name: A Kootz - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-numpy.jpeg - tags: - packages: - - numpy - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Matplotlib - url: https://youtu.be/EiPRIdHQEmE - description: | - Recording from the Python Tutorial Seminar Series introducing the Python Package `matplotlib`. The content to follow along with this video is hosted on this Matplotlib Tutorial GitHub Repository. - authors: - - name: Project Pythia - - name: Anissa Zacharias - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-matplotlib.jpeg - tags: - packages: - - matplotlib - formats: - - video - - tutorial - domains: - - data visualization - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Object Oriented Programming - url: https://youtu.be/GEFnL8C62u8 - description: | - Recording from the Python Tutorial Seminar Series introducing Object Oriented Programming. The content to follow along with this video is hosted in this Object Oriented Programming Tutorial GitHub Repository. - authors: - - name: Project Pythia - - name: A Kootz - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-oop.jpeg - tags: - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Cartopy - url: https://youtu.be/ivmd3RluMiw - description: | - Recording from the Python Tutorial Seminar Series introducing the Python Package `cartopy`. The content to follow along with this video is hosted in this Cartopy Tutorial GitHub Repository. - authors: - - name: Project Pythia - - name: Michaela Sizemore - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-cartopy.jpeg - tags: - packages: - - matplotlib - - cartopy - formats: - - video - - tutorial - domains: - - data visualization - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Git and GitHub - url: https://youtu.be/fYkPn0Nttlg - description: | - Recording from the Python Tutorial Seminar Series introducing the tools Git and GitHub. The content to follow along with this tutorial is hosted in this Git and GitHub Demo GitHub Repository. - authors: - - name: Project Pythia - - name: Kevin Paul - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-git.png - tags: - packages: - - git - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Pandas - url: https://youtu.be/BsV3ek7qsiM - description: | - Recording from the Python Tutorial Seminar Series introducing the Python Package `pandas`. The content to follow along with this video is hosted in this Pandas Tutorial GitHub Repository. - authors: - - name: Project Pythia - - name: Max Grover - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - - name: Drew Camron - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-pandas.jpeg - tags: - packages: - - pandas - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Xarray Part 1 - url: https://youtu.be/Ss4ryKukhi4 - description: | - Recording from the Python Tutorial Seminar Series introducing the Python Package `xarray`. This is the first lesson of a two part series. The content to follow along with this video is hosted in this Xarray Tutorial GitHub Repository. - authors: - - name: Project Pythia - - name: Anderson Banihirwe - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-xarray1.jpeg - tags: - packages: - - xarray - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Xarray Part 2 - url: https://youtu.be/2H_4drBwORY - description: | - Recording from the Python Tutorial Seminar Series introducing the Python Package `xarray`. This is the second lesson of a two part series. The content to follow along with this video is hosted in this Xarray Tutorial GitHub Repository. - authors: - - name: Project Pythia - - name: Anderson Banihirwe - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-xarray2.jpeg - tags: - packages: - - xarray - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Dask Part 1 - url: https://youtu.be/wn-QM6QUB_U - description: | - Recording from the Python Tutorial Seminar Series introducing the Python Package `dask`. This is the first lesson of a two part series. The content to follow along with this video is hosted in this Xarray Tutorial GitHub Repository. - authors: - - name: Project Pythia - - name: Anderson Banihirwe - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-dask1.jpeg - tags: - packages: - - dask - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Dask Part 2 - url: https://youtu.be/yn4_-1pHC5k - description: | - Recording from the Python Tutorial Seminar Series introducing the Python Package `dask`. This is the second lesson of a two part series. The content to follow along with this video is hosted in this Xarray Tutorial GitHub Repository. - authors: - - name: Project Pythia - - name: Anderson Banihirwe - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-dask2.jpeg - tags: - packages: - - dask - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - Plotting with GeoCAT - url: https://youtu.be/It231le1fAU - description: | - Recording from the Python Tutorial Seminar Series introducing advanced plotting techniques and highlighting tools developed by GeoCAT. The content to follow along with this video is hosted in this Plotting with GeoCat GitHub Repository. - authors: - - name: Project Pythia - - name: Anissa Zacharias - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-geocatplot.jpeg - tags: - packages: - - geocat-viz - formats: - - video - - tutorial - domains: - - data visualization - affiliation: - - pythia - - xdev - -- title: Python Tutorial Seminar Series - GeoCAT-Comp - url: https://www.youtube.com/watch?v=uiWDQKI8YTQ&t=6s - description: | - Recording from the Python Tutorial Seminar Series introducing `geocat-comp`. The content to follow along with this video is hosted in this GeoCat-Comp GitHub Repository. - authors: - - name: Project Pythia - - name: A Kootz - affiliation: NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/ptss-geocatcomp.jpeg - tags: - packages: - - geocat-comp - formats: - - video - - tutorial - affiliation: - - pythia - - xdev - -- title: The Pythia Foundations Book - url: https://foundations.projectpythia.org/landing-page.html - description: | - Brought to you by Project Pythia, this growing collection covers the foundational skills everyone needs to get started with scientific computing in the open-source Python ecosystem. - authors: - - name: Project Pythia - email: projectpythia@ucar.edu - url: https://projectpythia.org/ - thumbnail: /_static/thumbnails/ProjectPythia_Blue.png - tags: - formats: - - book - affiliation: - - pythia - -- title: Pythia Foundations - Getting started with Python - url: https://foundations.projectpythia.org/foundations/getting-started-python.html - description: | - This chapter of the Pythia Foundations book covers Python spin-up for new users. Here you will look at your first Python code and learn to run/install Python on various platforms. - authors: - - name: Project Pythia - email: projectpythia@ucar.edu - url: https://projectpythia.org/ - thumbnail: /_static/thumbnails/ProjectPythia_Blue.png - tags: - formats: - - book - affiliation: - - pythia - packages: - - pure python - -- title: Pythia Foundations - Getting started with Jupyter - url: https://foundations.projectpythia.org/foundations/getting-started-jupyter.html - description: | - This chapter of the Pythia Foundations book covers Python spin-up using Jupyter. Here you will learn about the JupyterLab interface and markdown formatting. - authors: - - name: Project Pythia - email: projectpythia@ucar.edu - url: https://projectpythia.org/ - thumbnail: /_static/thumbnails/ProjectPythia_Blue.png - tags: - formats: - - book - affiliation: - - pythia - packages: - - jupyter - -- title: Pythia Foundations - Getting started with GitHub - url: https://foundations.projectpythia.org/foundations/getting-started-github.html - description: | - This chapter of the Pythia Foundations book covers GitHub: what it is, basic version control, and how to open a pull request. - authors: - - name: Project Pythia - email: projectpythia@ucar.edu - url: https://projectpythia.org/ - thumbnail: /_static/thumbnails/ProjectPythia_Blue.png - tags: - formats: - - book - affiliation: - - pythia - packages: - - git - -- title: Pythia Foundations - Numpy - url: https://foundations.projectpythia.org/core/numpy.html - description: | - This chapter of the Pythia Foundations book covers the Python package NumPy: NumPy basics, intermediate NumPy, and NumPy broadcasting. - authors: - - name: Project Pythia - email: projectpythia@ucar.edu - url: https://projectpythia.org/ - thumbnail: /_static/thumbnails/ProjectPythia_Blue.png - tags: - formats: - - book - affiliation: - - pythia - packages: - - numpy - -- title: Pythia Foundations - Matplotlib - url: https://foundations.projectpythia.org/core/matplotlib.html - description: | - This chapter of the Pythia Foundations book covers basics of the Python package Matplotlib. - authors: - - name: Project Pythia - email: projectpythia@ucar.edu - url: https://projectpythia.org/ - thumbnail: /_static/thumbnails/ProjectPythia_Blue.png - tags: - formats: - - book - affiliation: - - pythia - packages: - - matplotlib - domains: - - data visualization - -- title: Pythia Foundations - Cartopy - url: https://foundations.projectpythia.org/core/cartopy.html - description: | - This chapter of the Pythia Foundations book introduces the Python package Cartopy, a package designed for geospatial data processing and used for its ability to produce maps. - authors: - - name: Project Pythia - email: projectpythia@ucar.edu - url: https://projectpythia.org/ - thumbnail: /_static/thumbnails/ProjectPythia_Blue.png - tags: - formats: - - book - affiliation: - - pythia - packages: - - cartopy - domains: - - data visualization - -- title: Pythia Foundations - Datetime - url: https://foundations.projectpythia.org/core/datetime.html - description: | - This section of the Pythia Foundations book contains tutorials on dealing with times and calendars in scientific Python, beginning with use of the datetime standard library. - authors: - - name: Project Pythia - email: projectpythia@ucar.edu - url: https://projectpythia.org/ - thumbnail: /_static/thumbnails/ProjectPythia_Blue.png - tags: - formats: - - book - affiliation: - - pythia - packages: - - datetime - -- title: Pythia Foundations - Pandas - url: https://foundations.projectpythia.org/core/pandas.html - description: | - This section of the Pythia Foundations book covers Pandas, a very powerful library for working with tabular data (i.e. anything you might put in a spreadsheet – a common data type in the geosciences). - authors: - - name: Project Pythia - email: projectpythia@ucar.edu - url: https://projectpythia.org/ - thumbnail: /_static/thumbnails/ProjectPythia_Blue.png - tags: - formats: - - book - affiliation: - - pythia - packages: - - pandas - -- title: Pythia Foundations - Data Formats - url: https://foundations.projectpythia.org/core/pandas.html - description: | - This section of the Pythia Foundations book covers how to interact in Python with data file formats in widespread use in the geosciences, such as NetCDF. - authors: - - name: Project Pythia - email: projectpythia@ucar.edu - url: https://projectpythia.org/ - thumbnail: /_static/thumbnails/ProjectPythia_Blue.png - tags: - formats: - - book - affiliation: - - pythia - domains: - - data formats - packages: - - netCDF4 - -- title: Pythia Foundations - Xarray - url: https://foundations.projectpythia.org/core/xarray.html - description: | - This section of the Pythia Foundations book contains tutorials on using Xarray. Xarray is used widely in the geosciences and beyond for analysis of gridded N-dimensional datasets. - authors: - - name: Project Pythia - email: projectpythia@ucar.edu - url: https://projectpythia.org/ - thumbnail: /_static/thumbnails/ProjectPythia_Blue.png - tags: - formats: - - book - affiliation: - - pythia - packages: - - xarray - -- title: Climatematch Academy - url: https://academy.climatematch.io/about - description: | - Climatematch Academy (CMA) is a wide-reaching, inclusive and approachable program aimed to introduce computational methods for climate science. CMA strives to create a globally diverse climate sciences community, trained on cutting edge techniques to access and analyze open-source modeled and observational climate data. - authors: - - name: Climatematch Team - url: https://academy.climatematch.io/about/team - thumbnail: /_static/thumbnails/climatematch.png - tags: - formats: - - course - domains: - - climate science - affiliation: - - neuromatch - - pythia - -- title: Plotting Earth Science Data - url: https://ploting-earth-science-data.web.app/ - description: | - A guide to analyze and plot Earth Science data for Scientist of all programming skill levels, using L-1 data product from CYGNSS mission as an example. - authors: - - name: Michael Nguyen - tags: - packages: - - cartopy - - xarray - - numpy - - matplotlib - - netCDF4 - domains: - - data visualization - - remote sensing - -- title: 'Herbie: Retrieve NWP Model Data' - url: https://herbie.readthedocs.io/ - description: | - Herbie is a python package that downloads recent and archived numerical weather prediction (NWP) model output from different cloud archive sources. Its most popular capability is to download HRRR model data. NWP data in the GRIB2 format can be read with xarray+cfgrib. - authors: - - name: Brian Blaylock - tags: - packages: - - xarray - - cfgrib - formats: - - documentation - - tutorial - domains: - - atmospheric science - - numerical weather prediction - -- title: The Earth Science Box Modelling Toolkit - url: https://github.com/uliw/esbmtk - description: | - ESBMTK is a python library that aims to simplify typical box modeling projects in the Earth-Sciences. - It uses a declarative approach to describe models, and abstracts typical modeling tasks like gas-exchange and carbonate dissolution through python classes. See the - manual at https://esbmtk.readthedocs.io/en/latest for installation instructions usage, and example code. - thumbnail: https://raw.githubusercontent.com/uliw/esbmtk/staging/mpc.png - authors: - - name: Ulrich G Wortmann - affiliation: Dept. of Earth Sciences, University of Toronto - affiliation_url: https://www.es.utoronto.ca/ - email: uli.wortmann@utoronto.ca - tags: - packages: - - esbmtk - - matplotlib - - numpy - - pandas - formats: - - code - domains: - - Earth System Science - - Chemical Oceanography - -- title: The PO.DAAC Cookbook - url: https://podaac.github.io/tutorials/ - description: | - A place to find NASA ocean, climate, and surface water data recipes and tutorials for the Physical Oceanography Distributed Active Archive Center (PO.DAAC) datasets, tools & services! - thumbnail: https://raw.githubusercontent.com/podaac/tutorials/master/images/podaac_cookbook_thumbnail.png - authors: - - name: PO.DAAC Team - affiliation: Jet Propulsion Laboratory, California Institute of Technology - affiliation_url: https://www.jpl.nasa.gov/ - tags: - packages: - - earthaccess - - xarray - formats: - - book - - tutorial - domains: - - Remote sensing - - Oceanography - - Hydrology - affiliation: - - Physical Oceanography Distributed Active Archive Center (PO.DAAC) - - Jet Propulsion Laboratory - - California Institute of Technology - -- title: UXarray - url: https://uxarray.readthedocs.io/ - description: Xarray extension for unstructured climate and global weather data - thumbnail: https://raw.githubusercontent.com/UXARRAY/uxarray/921dd8f42f661692f0cec170ee5c7dbedb294237/docs/_static/images/logos/uxarray_logo_v_dark.svg - authors: - - name: The UXarray Developers - affiliation: NSF NCAR & Department of Energy - tags: - packages: - - xarray - - dask - - numba - - numpy - - holoviews - - matplotlib - formats: - - documentation - - tutorial - domains: - - Unstructured Grids - - Data Analysis - - Data Visualization - - Atmospheric Science - - Climate Science - affiliation: - - Pangeo - - Raijin - - SEATS - -- title: GeoCAT Applications - url: https://ncar.github.io/geocat-applications/ - description: | - GeoCAT Applications is a community resource managed by the GeoCAT team. Inspired by the NCL Applications page, this is designed to be a quick reference demonstrating capabilities within the Scientific Python Ecosystem that may be relevant to your geoscience workflows. - authors: - - name: GeoCAT Team - url: https://geocat.ucar.edu/ - affiliation: NSF NCAR - affiliation_url: https://ncar.ucar.edu/ - thumbnail: /_static/thumbnails/geocat.png - tags: - formats: - - documentation diff --git a/portal/style.css b/portal/style.css new file mode 100644 index 000000000..e69de29bb diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 99227ca84..000000000 --- a/pyproject.toml +++ /dev/null @@ -1,15 +0,0 @@ -[tool.black] -line-length = 120 -target-version = ['py38'] -skip-string-normalization = true - -[tool.nbqa.config] -isort = "setup.cfg" -black = "pyproject.toml" - -[tool.nbqa.mutate] -black = 1 -pyupgrade = 1 - -[tool.nbqa.addopts] -pyupgrade = ["--py36-plus"] diff --git a/setup.cfg b/setup.cfg index 870b89f26..a0e8cb2d2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,7 +7,7 @@ select = B,C,E,F,W,T4,B9 [isort] known_first_party= -known_third_party=frontmatter,gallery_generator,markdown_it,pydantic,truncatehtml,yaml +known_third_party= multi_line_output=3 include_trailing_comma=True force_grid_wrap=0