From 4e298ffa65d64aaedafca62315f63ec7ac7ea378 Mon Sep 17 00:00:00 2001 From: Frank Harkins Date: Wed, 1 May 2024 18:52:58 +0100 Subject: [PATCH] Fix submission process (#701) ### Summary The submission process was broken as some entries in the issue template were being ignored by the parser. This PR fixes that and makes the submission process more robust so similar issues don't occur in the future. ### Details Currently, to add a new field to the issue template, you need to edit four different files to make sure the field makes it through to the database. This PR avoids that by explicitly mapping field IDs in the issue template to attributes of the repository model and writing the entry immediately. After this change, you only need to edit the issue template and the repository model. I've also added a test check the fields in the issue template match fields in the repository model. --------- Co-authored-by: Eric Arellano <14852634+Eric-Arellano@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/submission.yml | 8 +- .github/workflows/ecosystem-submission.yml | 25 +---- ecosystem/cli/ci.py | 41 +++---- ecosystem/models/repository.py | 12 +- ecosystem/resources/members/QPong.toml | 1 - ecosystem/resources/members/QiskitOpt.jl.toml | 1 - ecosystem/resources/members/QoptKIT.toml | 1 - .../resources/members/Quantum-Glasses.toml | 1 - ecosystem/resources/members/RasQberry.toml | 1 - .../members/circuit-knitting-toolbox.toml | 1 - ecosystem/resources/members/dense-ev.toml | 1 - ecosystem/resources/members/dsm-swap.toml | 2 - ecosystem/resources/members/dynacir.toml | 2 - ...tronic-structure-orbital-optimization.toml | 1 - ecosystem/resources/members/ffsim.toml | 2 - ecosystem/resources/members/mirror-gates.toml | 1 - ecosystem/resources/members/openqasm.toml | 2 - .../resources/members/purplecaffeine.toml | 2 - .../resources/members/pyRiemann-qiskit.toml | 1 - ecosystem/resources/members/qdao.toml | 1 - .../resources/members/qiskit-algorithms.toml | 1 - .../members/qiskit-aqt-provider.toml | 1 - .../resources/members/qiskit-bip-mapper.toml | 1 - .../members/qiskit-classroom-converter.toml | 3 - .../resources/members/qiskit-classroom.toml | 3 - .../members/qiskit-metal-docker.toml | 2 - .../qiskit-nature-pyscf-dft-embedding.toml | 2 - .../members/qiskit-nature-pyscf.toml | 2 - ecosystem/resources/members/qiskit-qec.toml | 3 - .../resources/members/qiskit-qubit-reuse.toml | 2 - .../resources/members/qiskit-qulacs.toml | 1 - .../resources/members/qiskit-research.toml | 3 - ecosystem/resources/members/qiskit-symb.toml | 2 - ecosystem/resources/members/qiskit-toqm.toml | 2 - ecosystem/resources/members/qlasskit.toml | 1 - ecosystem/resources/members/qmuvi.toml | 1 - ecosystem/resources/members/quPython.toml | 2 - .../members/quantum-prototype-template.toml | 2 - .../resources/members/quantum-serverless.toml | 1 - .../resources/members/quantum-tetris.toml | 3 - .../members/sat-circuits-engine.toml | 2 - ecosystem/resources/members/spinoza.toml | 2 - ecosystem/utils/submission_parser.py | 103 +++++++++++------- requirements.txt | 1 + tests/resources/issue.md | 4 + tests/resources/issue_2.md | 4 + tests/test_cli.py | 84 ++++++-------- tests/utils/test_utils.py | 29 ++++- 48 files changed, 160 insertions(+), 214 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/submission.yml b/.github/ISSUE_TEMPLATE/submission.yml index 27226b4326..72f74b077a 100644 --- a/.github/ISSUE_TEMPLATE/submission.yml +++ b/.github/ISSUE_TEMPLATE/submission.yml @@ -5,6 +5,8 @@ labels: ["submission"] assignees: - octocat body: + # The IDs of each input must correspond with the attributes of the repository model + # in ecosystem/models/repository.py - type: markdown attributes: value: | @@ -29,7 +31,7 @@ body: validations: required: true - type: input - id: repo + id: url attributes: label: Github repo description: Link to GitHub repo of project you want to submit @@ -53,7 +55,7 @@ body: validations: required: true - type: input - id: contacts + id: contact_info attributes: label: Email placeholder: awesome.person@example.org @@ -68,7 +70,7 @@ body: validations: required: false - type: dropdown - id: license + id: licence attributes: label: License description: License for your project diff --git a/.github/workflows/ecosystem-submission.yml b/.github/workflows/ecosystem-submission.yml index c00eb5c249..e7445b8fff 100644 --- a/.github/workflows/ecosystem-submission.yml +++ b/.github/workflows/ecosystem-submission.yml @@ -37,35 +37,14 @@ jobs: id: parse-issue env: ISSUE_BODY: ${{ github.event.issue.body }} - run: python manager.py ci parser_issue --body="$ISSUE_BODY" - - name: Add member - env: - SUBMISSION_NAME: ${{ steps.parse-issue.outputs.SUBMISSION_NAME }} - SUBMISSION_REPO: ${{ steps.parse-issue.outputs.SUBMISSION_REPO }} - SUBMISSION_DESCRIPTION: ${{ steps.parse-issue.outputs.SUBMISSION_DESCRIPTION }} - SUBMISSION_LICENCE: ${{ steps.parse-issue.outputs.SUBMISSION_LICENCE }} - SUBMISSION_CONTACT: ${{ steps.parse-issue.outputs.SUBMISSION_CONTACT }} - SUBMISSION_ALTERNATIVES: ${{ steps.parse-issue.outputs.SUBMISSION_ALTERNATIVES }} - SUBMISSION_AFFILIATIONS: ${{ steps.parse-issue.outputs.SUBMISSION_AFFILIATIONS }} - SUBMISSION_LABELS: ${{ steps.parse-issue.outputs.SUBMISSION_LABELS }} - SUBMISSION_WEBSITE: ${{ steps.parse-issue.outputs.SUBMISSION_WEBSITE }} - run: | - python manager.py members add_repo_2db --repo_name="$SUBMISSION_NAME" \ - --repo_link="$SUBMISSION_REPO" \ - --repo_description="$SUBMISSION_DESCRIPTION" \ - --repo_licence="$SUBMISSION_LICENCE" \ - --repo_contact="$SUBMISSION_CONTACT" \ - --repo_alt="$SUBMISSION_ALTERNATIVES" \ - --repo_affiliations="$SUBMISSION_AFFILIATIONS" \ - --repo_labels="$SUBMISSION_LABELS" \ - --repo_website="$SUBMISSION_WEBSITE" + run: python manager.py ci add_member_from_issue --body="$ISSUE_BODY" # Create PR - name: Commit changes and create pull request id: cpr uses: peter-evans/create-pull-request@v5 with: - commit-message: Submission - Add ${{ steps.parse-issue.outputs.SUBMISSION_REPO }} to list. + commit-message: Submission - Add ${{ steps.parse-issue.outputs.SUBMISSION_NAME }} to list. title: Add ${{ steps.parse-issue.outputs.SUBMISSION_NAME }} to list. body: | Add ${{ steps.parse-issue.outputs.SUBMISSION_NAME }} to list. diff --git a/ecosystem/cli/ci.py b/ecosystem/cli/ci.py index 9820abd038..3bb0199277 100644 --- a/ecosystem/cli/ci.py +++ b/ecosystem/cli/ci.py @@ -1,9 +1,11 @@ """CliCI class for controlling all CLI functions.""" -import os -from typing import Optional +from __future__ import annotations + + +from pathlib import Path from ecosystem.daos import DAO -from ecosystem.utils import logger, parse_submission_issue +from ecosystem.utils import parse_submission_issue from ecosystem.utils.utils import set_actions_output @@ -17,37 +19,20 @@ class CliCI: Ex: `python manager.py ci parser_issue --body=""` """ - def __init__(self, root_path: Optional[str] = None): - """CliCI class.""" - self.current_dir = root_path or os.path.abspath(os.getcwd()) - self.resources_dir = "{}/ecosystem/resources".format(self.current_dir) - self.dao = DAO(path=self.resources_dir) - self.logger = logger - @staticmethod - def parser_issue(body: str) -> None: - """Command for calling body issue parsing function. + def add_member_from_issue(body: str, *, resources_dir: str | None = None) -> None: + """Parse an issue created from the issue template and add the member to the database Args: body: body of the created issue + resources_dir: (For testing) Path to the working directory Returns: - logs output - We want to give the result of the parsing issue to the GitHub action + None (side effect is updating database and writing actions output) """ - parsed_result = parse_submission_issue(body) + resources_dir = Path(resources_dir or (Path.cwd() / "ecosystem/resources")) - to_print = [ - ("SUBMISSION_NAME", parsed_result.name), - ("SUBMISSION_REPO", parsed_result.url), - ("SUBMISSION_DESCRIPTION", parsed_result.description), - ("SUBMISSION_LICENCE", parsed_result.licence), - ("SUBMISSION_CONTACT", parsed_result.contact_info), - ("SUBMISSION_ALTERNATIVES", parsed_result.alternatives), - ("SUBMISSION_AFFILIATIONS", parsed_result.affiliations), - ("SUBMISSION_LABELS", parsed_result.labels), - ("SUBMISSION_WEBSITE", parsed_result.website), - ] - - set_actions_output(to_print) + parsed_result = parse_submission_issue(body) + DAO(path=resources_dir).write(parsed_result) + set_actions_output([("SUBMISSION_NAME", parsed_result.name)]) diff --git a/ecosystem/models/repository.py b/ecosystem/models/repository.py index 7f40e871be..e5f6ed1990 100644 --- a/ecosystem/models/repository.py +++ b/ecosystem/models/repository.py @@ -10,12 +10,16 @@ @dataclass class Repository(JsonSerializable): - """Main repository class.""" + """Main repository class. + + NOTE: These attribute names must correspond to field IDs in the issue + template (.github/ISSUE_TEMPLATE/submission.yml). + """ # pylint: disable=too-many-instance-attributes - name: str - url: str - description: str + name: str | None = None + url: str | None = None + description: str | None = None licence: str | None = None contact_info: str | None = None alternatives: str | None = None diff --git a/ecosystem/resources/members/QPong.toml b/ecosystem/resources/members/QPong.toml index cff8f818c3..4891d7170c 100644 --- a/ecosystem/resources/members/QPong.toml +++ b/ecosystem/resources/members/QPong.toml @@ -3,7 +3,6 @@ url = "https://github.com/HuangJunye/QPong" description = "A quantum version of the classic game Pong built with Qiskit and PyGame" licence = "Apache 2.0" contact_info = "ukskosana@gmail.com" -alternatives = "_No response_" labels = [ "Game",] created_at = 1678827877.979398 updated_at = 1678827877.979398 diff --git a/ecosystem/resources/members/QiskitOpt.jl.toml b/ecosystem/resources/members/QiskitOpt.jl.toml index 0c35962164..370ecfa4e7 100644 --- a/ecosystem/resources/members/QiskitOpt.jl.toml +++ b/ecosystem/resources/members/QiskitOpt.jl.toml @@ -3,7 +3,6 @@ url = "https://github.com/psrenergy/QiskitOpt.jl" description = "QiskitOpt.jl is a Julia package that exports a JuMP wrapper for qiskit-optimization." licence = "MIT license" contact_info = "pedroxavier@psr-inc.com, pedroripper@psr-inc.com, tiago@psr-inc.com, joaquim@psr-inc.com, dbernalneira@usra.edu" -alternatives = "_No response_" affiliations = "@psrenergy and USRA" labels = [ "Algorithms", "Julia",] created_at = 1673642669.650459 diff --git a/ecosystem/resources/members/QoptKIT.toml b/ecosystem/resources/members/QoptKIT.toml index ba1ce0d660..63cfdf85af 100644 --- a/ecosystem/resources/members/QoptKIT.toml +++ b/ecosystem/resources/members/QoptKIT.toml @@ -3,7 +3,6 @@ url = "https://github.com/SOQCSAdmin/QoptKIT" description = "Optical circuits in Qiskit. Translate Qiskit circuits to quantum-optical circuits made of phase shifters and beamsplitters, simulate the circuit, then translate the outcome back to a qubit encoding." licence = "Apache License 2.0" contact_info = "soqcslib@gmail.com" -alternatives = "_No response_" affiliations = "National University of Ireland Maynooth" labels = [ "Circuit simulator",] stars = 1 diff --git a/ecosystem/resources/members/Quantum-Glasses.toml b/ecosystem/resources/members/Quantum-Glasses.toml index 840da08237..05713ede69 100644 --- a/ecosystem/resources/members/Quantum-Glasses.toml +++ b/ecosystem/resources/members/Quantum-Glasses.toml @@ -4,7 +4,6 @@ description = "Visualise the effects of single-qubit gates on a qubit via Bloch licence = "Apache License 2.0" contact_info = "shahj097@gmail.com" alternatives = "The only alternative is to code up a circuit (qiskit.QuantumCircuit) and pass the circuit to visualize_transition from qiskit.visualization. Quantum Glasses implementation wraps all of this into a simple, easy to use Tkinter Software." -affiliations = "_No response_" labels = [ "Visualization",] stars = 15 group = "other" diff --git a/ecosystem/resources/members/RasQberry.toml b/ecosystem/resources/members/RasQberry.toml index 211ee0391b..de70317585 100644 --- a/ecosystem/resources/members/RasQberry.toml +++ b/ecosystem/resources/members/RasQberry.toml @@ -3,7 +3,6 @@ url = "https://github.com/JanLahmann/RasQberry" description = "RasQberry is a functional model of IBM Quantum System One, and can run Qiskit on the integrated Raspberry Pi" licence = "Apache License 2.0" contact_info = "Jan.lahmann@de.ibm.com" -alternatives = "_No response_" labels = [ "Game",] created_at = 1674598082.072493 updated_at = 1674598082.072494 diff --git a/ecosystem/resources/members/circuit-knitting-toolbox.toml b/ecosystem/resources/members/circuit-knitting-toolbox.toml index fc907c5289..e7027c2fa7 100644 --- a/ecosystem/resources/members/circuit-knitting-toolbox.toml +++ b/ecosystem/resources/members/circuit-knitting-toolbox.toml @@ -3,7 +3,6 @@ url = "https://github.com/Qiskit-Extensions/circuit-knitting-toolbox" description = "Decompose large circuits into smaller, hardware-executable circuits, then use their results to reconstruct the original circuit's outcome. This toolbox includes entanglement forging, circuit knitting, and classical embedding." licence = "Apache License 2.0" contact_info = "blake.johnson@ibm.com" -alternatives = "_No response_" labels = [ "Algorithms", "Circuit",] created_at = 1670427445.884067 updated_at = 1670427445.884068 diff --git a/ecosystem/resources/members/dense-ev.toml b/ecosystem/resources/members/dense-ev.toml index 6585dc5758..3a8c4dd62e 100644 --- a/ecosystem/resources/members/dense-ev.toml +++ b/ecosystem/resources/members/dense-ev.toml @@ -3,7 +3,6 @@ url = "https://github.com/atlytle/dense-ev" description = "Implements expectation value measurements in Qiskit using optimal dense grouping. Dense-ev provides an improvement of ~2^m over naive grouping and (3/2)^m over qubit-wise commuting groups." licence = "Apache License 2.0" contact_info = "atlytle@gmail.com" -alternatives = "_No response_" affiliations = "University of Illinois at Urbana-Champaign" labels = [ "Paper implementation",] stars = 7 diff --git a/ecosystem/resources/members/dsm-swap.toml b/ecosystem/resources/members/dsm-swap.toml index 783b30838a..96892b6d3e 100644 --- a/ecosystem/resources/members/dsm-swap.toml +++ b/ecosystem/resources/members/dsm-swap.toml @@ -2,8 +2,6 @@ name = "dsm-swap" url = "https://github.com/qiskit-community/dsm-swap" description = "A doubly stochastic matrices-based approach to optimal qubit routing." licence = "Apache License 2.0" -contact_info = "_No response_" -alternatives = "_No response_" labels = [ "Paper implementation", "Circuit",] created_at = 1678827878.56605 updated_at = 1678827878.566051 diff --git a/ecosystem/resources/members/dynacir.toml b/ecosystem/resources/members/dynacir.toml index 9baba54f06..a56be7166c 100644 --- a/ecosystem/resources/members/dynacir.toml +++ b/ecosystem/resources/members/dynacir.toml @@ -2,8 +2,6 @@ name = "dynacir" url = "https://github.com/derek-wang-ibm/dynacir" description = "Dynacir is a transpilation plugin for Qiskit that optimizes dynamic quantum circuits." licence = "Apache License 2.0" -contact_info = "_No response_" -alternatives = "_No response_" affiliations = "IBM Quantum" labels = [ "Optimization", "Prototype",] stars = 0 diff --git a/ecosystem/resources/members/electronic-structure-orbital-optimization.toml b/ecosystem/resources/members/electronic-structure-orbital-optimization.toml index 9c4836c2b8..6644f42c3f 100644 --- a/ecosystem/resources/members/electronic-structure-orbital-optimization.toml +++ b/ecosystem/resources/members/electronic-structure-orbital-optimization.toml @@ -3,7 +3,6 @@ url = "https://github.com/JoelHBierman/electronic-structure-orbital-optimization description = "Apply orbital optimization to quantum algorithms that solve electronic structure Hamiltonians. This collection of methods optimize the underlying basis set through orbital rotations, finding more accurate energies with fewer qubits." licence = "Apache License 2.0" contact_info = "joel.bierman@duke.edu" -alternatives = "_No response_" affiliations = "Duke University" labels = [ "Algorithms", "Chemistry",] stars = 0 diff --git a/ecosystem/resources/members/ffsim.toml b/ecosystem/resources/members/ffsim.toml index 8042695308..737aa8ae63 100644 --- a/ecosystem/resources/members/ffsim.toml +++ b/ecosystem/resources/members/ffsim.toml @@ -2,8 +2,6 @@ name = "ffsim" url = "https://github.com/qiskit-community/ffsim" description = "ffsim is a software library for simulating fermionic quantum circuits that conserve particle number and the Z component of spin." licence = "Apache License 2.0" -contact_info = "_No response_" -alternatives = "_No response_" affiliations = "IBM" labels = [ "Chemistry", "Circuit simulator", "IBM maintained", "Rust",] website = "https://qiskit-community.github.io/ffsim/" diff --git a/ecosystem/resources/members/mirror-gates.toml b/ecosystem/resources/members/mirror-gates.toml index ef5ef20a5d..ab8573efc4 100644 --- a/ecosystem/resources/members/mirror-gates.toml +++ b/ecosystem/resources/members/mirror-gates.toml @@ -3,7 +3,6 @@ url = "https://github.com/Pitt-JonesLab/mirror-gates" description = "MIRAGE is a transpilation plugin for quantum circuits that minimizes the use of SWAP gates while optimizing native basis gate decomposition through mirror gates. Specifically designed for iSWAP-based quantum systems, MIRAGE improves circuit depth, making quantum algorithms more practical and efficient." licence = "MIT license" contact_info = "evmckinney9@gmail.com" -alternatives = "_No response_" affiliations = "University of Pittsburgh" labels = [ "Paper implementation",] stars = 3 diff --git a/ecosystem/resources/members/openqasm.toml b/ecosystem/resources/members/openqasm.toml index c66654f1fa..42f23c6ca7 100644 --- a/ecosystem/resources/members/openqasm.toml +++ b/ecosystem/resources/members/openqasm.toml @@ -2,8 +2,6 @@ name = "OpenQASM" url = "https://github.com/openqasm/openqasm" description = "An imperative programming language for near-term quantum computing. Describe quantum programs by using the measurement-based quantum circuit model with support for classical feedforward flow control based on measurement outcomes." licence = "Apache 2.0" -contact_info = "_No response_" -alternatives = "_No response_" affiliations = "OpenQASM specification contributors" labels = [ "OpenQASM",] created_at = 1660223774.444544 diff --git a/ecosystem/resources/members/purplecaffeine.toml b/ecosystem/resources/members/purplecaffeine.toml index d4e8db0803..898db03793 100644 --- a/ecosystem/resources/members/purplecaffeine.toml +++ b/ecosystem/resources/members/purplecaffeine.toml @@ -2,8 +2,6 @@ name = "PurpleCaffeine" url = "https://github.com/IceKhan13/purplecaffeine" description = "Project is aimed to create simple general interface to track quantum experiments, store and search them in an easy way." licence = "Apache License 2.0" -contact_info = "_No response_" -alternatives = "_No response_" affiliations = "QAMP" labels = [ "Productivity", "Jupyter notebook",] created_at = 1688661859.080258 diff --git a/ecosystem/resources/members/pyRiemann-qiskit.toml b/ecosystem/resources/members/pyRiemann-qiskit.toml index 889ba336fd..0da2bc4534 100644 --- a/ecosystem/resources/members/pyRiemann-qiskit.toml +++ b/ecosystem/resources/members/pyRiemann-qiskit.toml @@ -3,7 +3,6 @@ url = "https://github.com/pyRiemann/pyRiemann-qiskit" description = "A library for supervised machine learning based on quantum computing and Riemannian geometry. The project is built on top of the Qiskit and pyRiemann projects and focuses on the classification of time series data." licence = "BSD 3-Clause \"New\" or \"Revised\" license" contact_info = "gcattan@hotmail.fr" -alternatives = "_No response_" affiliations = "pyRiemann (https://github.com/pyRiemann)" labels = [ "Machine learning",] stars = 21 diff --git a/ecosystem/resources/members/qdao.toml b/ecosystem/resources/members/qdao.toml index 764980901e..e0e12df7ca 100644 --- a/ecosystem/resources/members/qdao.toml +++ b/ecosystem/resources/members/qdao.toml @@ -3,7 +3,6 @@ url = "https://github.com/Zhaoyilunnn/qdao" description = "A lightweight framework to enable configurable memory consumption when simulating large quantum circuits." licence = "Apache License 2.0" contact_info = "zyilun8@gmail.com" -alternatives = "_No response_" affiliations = "Institute of Computing Technology, Chinese Academy of Sciences." labels = [ "Circuit simulator",] stars = 7 diff --git a/ecosystem/resources/members/qiskit-algorithms.toml b/ecosystem/resources/members/qiskit-algorithms.toml index f762f333e7..3bec38ae41 100644 --- a/ecosystem/resources/members/qiskit-algorithms.toml +++ b/ecosystem/resources/members/qiskit-algorithms.toml @@ -3,7 +3,6 @@ url = "https://github.com/qiskit-community/qiskit-algorithms" description = "A library of quantum algorithms for near-term quantum devices with short-depth circuits." licence = "Apache License 2.0" contact_info = "ept@zurich.ibm.com" -alternatives = "_No response_" affiliations = "IBM Quantum" labels = [ "Algorithms",] stars = 77 diff --git a/ecosystem/resources/members/qiskit-aqt-provider.toml b/ecosystem/resources/members/qiskit-aqt-provider.toml index 1aca54b7a3..9715f07c57 100644 --- a/ecosystem/resources/members/qiskit-aqt-provider.toml +++ b/ecosystem/resources/members/qiskit-aqt-provider.toml @@ -3,7 +3,6 @@ url = "https://github.com/qiskit-community/qiskit-aqt-provider" description = "Qiskit provider for ion-trap quantum computers from Alpine Quantum Technologies (AQT)." licence = "Apache License 2.0" contact_info = "info@aqt.eu" -alternatives = "_No response_" affiliations = "Alpine Quantum Technologies GmbH" labels = [ "Provider",] website = "https://www.aqt.eu/qc-systems/" diff --git a/ecosystem/resources/members/qiskit-bip-mapper.toml b/ecosystem/resources/members/qiskit-bip-mapper.toml index a04923f9c2..7033f63aac 100644 --- a/ecosystem/resources/members/qiskit-bip-mapper.toml +++ b/ecosystem/resources/members/qiskit-bip-mapper.toml @@ -3,7 +3,6 @@ url = "https://github.com/qiskit-community/qiskit-bip-mapper" description = "Solve the routing and layout problems as a binary integer programming (BIP) problem. This is an implementation of G. Nannicini et al. \"Optimal qubit assignment and routing via integer programming.\"" licence = "Apache License 2.0" contact_info = "itoko@jp.ibm.com" -alternatives = "_No response_" affiliations = "IBM" labels = [ "Paper implementation",] created_at = 1681306690.423614 diff --git a/ecosystem/resources/members/qiskit-classroom-converter.toml b/ecosystem/resources/members/qiskit-classroom-converter.toml index b0c1d9b876..e1e5ecf25b 100644 --- a/ecosystem/resources/members/qiskit-classroom-converter.toml +++ b/ecosystem/resources/members/qiskit-classroom-converter.toml @@ -2,9 +2,6 @@ name = "qiskit-classroom-converter" url = "https://github.com/KMU-quantum-classroom/qiskit-classroom-converter" description = "Convert quantum circuits, matrices, and bra-ket strings. This converter includes the following conversion functions: quantum circuit to bra-ket notation, quantum circuit to matrix, matrix to quantum circuit, bra-ket notation to matrix." licence = "Apache License 2.0" -contact_info = "_No response_" -alternatives = "_No response_" -affiliations = "_No response_" labels = [ "Converter",] website = "https://github.com/KMU-quantum-classroom" stars = 2 diff --git a/ecosystem/resources/members/qiskit-classroom.toml b/ecosystem/resources/members/qiskit-classroom.toml index 28a018d67c..51588e2aab 100644 --- a/ecosystem/resources/members/qiskit-classroom.toml +++ b/ecosystem/resources/members/qiskit-classroom.toml @@ -2,9 +2,6 @@ name = "qiskit-classroom" url = "https://github.com/KMU-quantum-classroom/qiskit-classroom" description = "Qiskit-classroom is a toolkit that helps implement quantum algorithms by converting and visualizing different expressions used in the Qiskit ecosystem using Qiskit-classroom-converter. The following three transformations are supported : Quantum circuit to Dirac notation, quantum circuit to matrix, matrix to quantum circuit etc." licence = "Apache License 2.0" -contact_info = "_No response_" -alternatives = "_No response_" -affiliations = "_No response_" labels = [ "Visualization",] website = "https://github.com/KMU-quantum-classroom" stars = 2 diff --git a/ecosystem/resources/members/qiskit-metal-docker.toml b/ecosystem/resources/members/qiskit-metal-docker.toml index 6efc8e319a..0f7d76cb87 100644 --- a/ecosystem/resources/members/qiskit-metal-docker.toml +++ b/ecosystem/resources/members/qiskit-metal-docker.toml @@ -3,8 +3,6 @@ url = "https://github.com/Dpbm/qiskit-metal-docker" description = "A Docker image to run Qiskit Metal projects." licence = "MIT license" contact_info = "dpbm136@gmail.com" -alternatives = "_No response_" -affiliations = "_No response_" labels = [ "Hardware",] website = "https://dpbm.github.io/qiskit-metal-docker/" stars = 0 diff --git a/ecosystem/resources/members/qiskit-nature-pyscf-dft-embedding.toml b/ecosystem/resources/members/qiskit-nature-pyscf-dft-embedding.toml index 4ddc59731e..c0847af219 100644 --- a/ecosystem/resources/members/qiskit-nature-pyscf-dft-embedding.toml +++ b/ecosystem/resources/members/qiskit-nature-pyscf-dft-embedding.toml @@ -3,8 +3,6 @@ url = "https://github.com/mrossinek/qiskit-nature-pyscf-dft-embedding" description = "This repository contains the latest prototype implementation of the Qiskit Nature + PySCF DFT Embedding. It is based on \"Quantum HF/DFT-embedding algorithms for electronic structure calculations: Scaling up to complex molecular systems\"." licence = "Apache License 2.0" contact_info = "oss@zurich.ibm.com" -alternatives = "_No response_" -affiliations = "_No response_" labels = [ "Algorithms", "Chemistry",] created_at = 1685540348.208874 updated_at = 1685540348.208879 diff --git a/ecosystem/resources/members/qiskit-nature-pyscf.toml b/ecosystem/resources/members/qiskit-nature-pyscf.toml index 0244d4bb17..f6baa6957f 100644 --- a/ecosystem/resources/members/qiskit-nature-pyscf.toml +++ b/ecosystem/resources/members/qiskit-nature-pyscf.toml @@ -2,8 +2,6 @@ name = "Qiskit Nature PySCF" url = "https://github.com/qiskit-community/qiskit-nature-pyscf" description = "A third-party integration plugin of Qiskit Nature and PySCF." licence = "Apache License 2.0" -contact_info = "_No response_" -alternatives = "_No response_" labels = [ "Chemistry",] created_at = 1678827878.723733 updated_at = 1678827878.723734 diff --git a/ecosystem/resources/members/qiskit-qec.toml b/ecosystem/resources/members/qiskit-qec.toml index 45eb215571..f98e3b4c86 100644 --- a/ecosystem/resources/members/qiskit-qec.toml +++ b/ecosystem/resources/members/qiskit-qec.toml @@ -2,9 +2,6 @@ name = "Qiskit QEC" url = "https://github.com/qiskit-community/qiskit-qec" description = "For quantum error correction developers, experimentalists, and theorists." licence = "Apache 2.0" -contact_info = "_No response_" -alternatives = "_No response_" -affiliations = "_No response_" labels = [ "Algorithms", "Error correction", "Circuit",] created_at = 1662992390.122591 updated_at = 1662992390.122597 diff --git a/ecosystem/resources/members/qiskit-qubit-reuse.toml b/ecosystem/resources/members/qiskit-qubit-reuse.toml index cbaba39037..fdc4c3132d 100644 --- a/ecosystem/resources/members/qiskit-qubit-reuse.toml +++ b/ecosystem/resources/members/qiskit-qubit-reuse.toml @@ -2,8 +2,6 @@ name = "qiskit-qubit-reuse" url = "https://github.com/qiskit-community/qiskit-qubit-reuse" description = "A Qiskit transpiler stage plugin that reuses qubits through mid-circuit measurement and reset." licence = "Apache License 2.0" -contact_info = "_No response_" -alternatives = "_No response_" affiliations = "IBM" labels = [ "Paper implementation",] stars = 11 diff --git a/ecosystem/resources/members/qiskit-qulacs.toml b/ecosystem/resources/members/qiskit-qulacs.toml index 2c1839de2a..a0b3db9e5a 100644 --- a/ecosystem/resources/members/qiskit-qulacs.toml +++ b/ecosystem/resources/members/qiskit-qulacs.toml @@ -3,7 +3,6 @@ url = "https://github.com/Gopal-Dahale/qiskit-qulacs" description = "Qiskit-Qulacs allows users to execute Qiskit programs using Qulacs backend." licence = "Apache License 2.0" contact_info = "dahalegopal27@gmail.com" -alternatives = "_No response_" affiliations = "Supported by the Unitary Fund microgrant program: https://unitary.fund/grants/" labels = [ "Circuit simulator", "Converter", "Provider",] stars = 8 diff --git a/ecosystem/resources/members/qiskit-research.toml b/ecosystem/resources/members/qiskit-research.toml index 6848a058c2..5227e049d4 100644 --- a/ecosystem/resources/members/qiskit-research.toml +++ b/ecosystem/resources/members/qiskit-research.toml @@ -2,9 +2,6 @@ name = "Qiskit Research" url = "https://github.com/qiskit-community/qiskit-research" description = "Run quantum computing research experiments by using Qiskit and IBM Quantum services, demonstrating best practices by example." licence = "Apache 2.0" -contact_info = "_No response_" -alternatives = "_No response_" -affiliations = "_No response_" labels = [ "Paper implementation",] created_at = 1662992208.202283 updated_at = 1662992208.20229 diff --git a/ecosystem/resources/members/qiskit-symb.toml b/ecosystem/resources/members/qiskit-symb.toml index 8c99074bf7..0d2f3da475 100644 --- a/ecosystem/resources/members/qiskit-symb.toml +++ b/ecosystem/resources/members/qiskit-symb.toml @@ -3,8 +3,6 @@ url = "https://github.com/SimoneGasperini/qiskit-symb" description = "Easy-to-use Python package designed to enable symbolic quantum computation in Qiskit. It provides the basic tools for the symbolic evaluation of statevectors, density matrices, and unitary operators directly created from parametric Qiskit quantum circuits. The implementation is based on the Sympy library as backend for symbolic expressions manipulation." licence = "Apache License 2.0" contact_info = "simone.gasperini4@unibo.it" -alternatives = "_No response_" -affiliations = "_No response_" labels = [ "Quantum information", "Circuit simulator",] created_at = 1680254616.930627 updated_at = 1680254616.930632 diff --git a/ecosystem/resources/members/qiskit-toqm.toml b/ecosystem/resources/members/qiskit-toqm.toml index 72454148a8..5bd6c25105 100644 --- a/ecosystem/resources/members/qiskit-toqm.toml +++ b/ecosystem/resources/members/qiskit-toqm.toml @@ -2,8 +2,6 @@ name = "Qiskit TOQM" url = "https://github.com/qiskit-toqm/qiskit-toqm" description = "Transpiler routing method that uses the Time-Optimal Qubit Mapping (TOQM) algorithm." licence = "Apache License 2.0" -contact_info = "_No response_" -alternatives = "_No response_" labels = [ "Paper implementation", "Circuit",] created_at = 1678827878.737541 updated_at = 1678827878.737541 diff --git a/ecosystem/resources/members/qlasskit.toml b/ecosystem/resources/members/qlasskit.toml index e45c0f1f34..62b8d93dc0 100644 --- a/ecosystem/resources/members/qlasskit.toml +++ b/ecosystem/resources/members/qlasskit.toml @@ -3,7 +3,6 @@ url = "https://github.com/dakk/qlasskit" description = "Qlasskit is a Python library that allows quantum developers to write classical algorithms in pure Python and translate them into quantum circuits." licence = "Apache License 2.0" contact_info = "gessadavide@gmail.com" -alternatives = "_No response_" affiliations = "The project is partially funded by the UnitaryFund microgrant." labels = [ "Converter",] website = "https://dakk.github.io/qlasskit/" diff --git a/ecosystem/resources/members/qmuvi.toml b/ecosystem/resources/members/qmuvi.toml index ed044ddf63..a9c389e8f5 100644 --- a/ecosystem/resources/members/qmuvi.toml +++ b/ecosystem/resources/members/qmuvi.toml @@ -3,7 +3,6 @@ url = "https://github.com/garymooney/qmuvi" description = "Convert circuits into audiovisual experiences, bridging the gap between complex quantum computations and human perception. Render music videos that reveal the evolution of quantum states, making quantum computing more intuitive and accessible." licence = "GNU Library or \"Lesser\" General Public License (LGPL)" contact_info = "gary.mooney4444@gmail.com" -alternatives = "_No response_" affiliations = "The University of Melbourne" labels = [ "Converter", "Visualization", "Education",] stars = 13 diff --git a/ecosystem/resources/members/quPython.toml b/ecosystem/resources/members/quPython.toml index 0b13342fa1..4a9f01f332 100644 --- a/ecosystem/resources/members/quPython.toml +++ b/ecosystem/resources/members/quPython.toml @@ -3,8 +3,6 @@ url = "https://github.com/frankharkins/quPython" description = "Write quantum programs as Python functions instead of circuit objects. Create higher-level quantum data types and return measurement results as bool-like objects." licence = "MIT license" contact_info = "frankharkins@hotmail.co.uk" -alternatives = "_No response_" -affiliations = "_No response_" labels = [ "Circuit", "Converter", "Productivity",] stars = 1 group = "other" diff --git a/ecosystem/resources/members/quantum-prototype-template.toml b/ecosystem/resources/members/quantum-prototype-template.toml index f06be41c72..9ce1e5f4bf 100644 --- a/ecosystem/resources/members/quantum-prototype-template.toml +++ b/ecosystem/resources/members/quantum-prototype-template.toml @@ -2,8 +2,6 @@ name = "Quantum Prototype Template" url = "https://github.com/qiskit-community/quantum-prototype-template" description = "A template repository for generating new quantum prototypes based on Qiskit." licence = "Apache License 2.0" -contact_info = "_No response_" -alternatives = "_No response_" affiliations = "Qiskit Community" labels = [ "Productivity",] stars = 38 diff --git a/ecosystem/resources/members/quantum-serverless.toml b/ecosystem/resources/members/quantum-serverless.toml index e8d2e71e13..0edb008982 100644 --- a/ecosystem/resources/members/quantum-serverless.toml +++ b/ecosystem/resources/members/quantum-serverless.toml @@ -3,7 +3,6 @@ url = "https://github.com/Qiskit-Extensions/quantum-serverless" description = "Execute Qiskit programs as long-running jobs and distribute them across many CPUs, GPUs, and QPUs." licence = "Apache License 2.0" contact_info = "blake.johnson@ibm.com" -alternatives = "_No response_" labels = [ "Software development kit",] created_at = 1670427445.627174 updated_at = 1670427445.627175 diff --git a/ecosystem/resources/members/quantum-tetris.toml b/ecosystem/resources/members/quantum-tetris.toml index 24ef6c268e..4bf75cc236 100644 --- a/ecosystem/resources/members/quantum-tetris.toml +++ b/ecosystem/resources/members/quantum-tetris.toml @@ -2,9 +2,6 @@ name = "Quantum Tetris" url = "https://github.com/olivierbrcknr/quantum-tetris" description = "What would happen if you combine Tetris with a Quantum computer? The winning entry of the Quantum Design Jam from IBM and Parsons in October 2021 explores just that!" licence = "Apache License 2.0" -contact_info = "_No response_" -alternatives = "_No response_" -affiliations = "_No response_" labels = [ "Game",] created_at = 1679712086.990215 updated_at = 1679712086.99022 diff --git a/ecosystem/resources/members/sat-circuits-engine.toml b/ecosystem/resources/members/sat-circuits-engine.toml index ae9b0c0e6e..347a5e96c1 100644 --- a/ecosystem/resources/members/sat-circuits-engine.toml +++ b/ecosystem/resources/members/sat-circuits-engine.toml @@ -3,8 +3,6 @@ url = "https://github.com/ohadlev77/sat-circuits-engine" description = "A Python-Qiskit-based package that provides capabilities of easily generating, executing and analyzing quantum circuits for satisfiability problems according to user-defined constraints. The circuits generated by the program are based on Grover's algorithm and its amplitude-amplification generalization." licence = "Apache License 2.0" contact_info = "ohadlev77@gmail.com" -alternatives = "_No response_" -affiliations = "_No response_" labels = [ "Algorithms", "Circuit",] created_at = 1678450437.835542 updated_at = 1678450437.835547 diff --git a/ecosystem/resources/members/spinoza.toml b/ecosystem/resources/members/spinoza.toml index e1c2c644fe..893f13188b 100644 --- a/ecosystem/resources/members/spinoza.toml +++ b/ecosystem/resources/members/spinoza.toml @@ -3,8 +3,6 @@ url = "https://github.com/smu160/spinoza" description = "One of the fastest open-source quantum state simulators. Spinoza has a Qiskit-like quantum circuit interface, runs on various computing environments, and supports multi-threading and distributed computing." licence = "Apache License 2.0" contact_info = "sy2685@columbia.edu" -alternatives = "_No response_" -affiliations = "_No response_" labels = [ "Circuit simulator", "Rust",] created_at = 1683727562.604629 updated_at = 1683727562.604634 diff --git a/ecosystem/utils/submission_parser.py b/ecosystem/utils/submission_parser.py index dea2eb5ad8..03ffa36500 100644 --- a/ecosystem/utils/submission_parser.py +++ b/ecosystem/utils/submission_parser.py @@ -1,24 +1,64 @@ """Parser for issue submission.""" -from collections import defaultdict + +from pathlib import Path import mdformat +import yaml from ecosystem.models.repository import Repository -def _clean_section(section: str) -> {str: str}: - """For a section, return a tuple with a title and "clean section". - A clean section is without new lines and strip spaces""" - paragraphs = section.split("\n") - section = (" ").join( - [paragraph.strip() for paragraph in paragraphs[1:] if paragraph] +def _parse_section(section: str, label_to_id: dict[str, str]) -> tuple[str, str]: + """For a section, return its field ID and the content. + The content has no newlines and has spaces stripped. + """ + lines = section.split("\n") + content = " ".join(line.strip() for line in lines[1:] if line) + label = lines[0].strip() + field_id = label_to_id[label] + return field_id, content + + +def _get_label_to_id_map() -> dict[str, str]: + """Create a dict that maps a fields "label" to its `id` from the issue + template + """ + issue_template = yaml.load( + Path(".github/ISSUE_TEMPLATE/submission.yml").read_text(), + Loader=yaml.SafeLoader, ) - title = paragraphs[0].strip() - return (title, section) + label_to_id = { + form["attributes"]["label"]: form["id"] + for form in issue_template["body"] + if form["type"] != "markdown" + } + return label_to_id def parse_submission_issue(body_of_issue: str) -> Repository: """Parse issue body. + The GitHub issue is a collection of "fields", each of which has a + "label" and an "ID" specified in the template. We require IDs in the + template to match argument names of the Repository model. + + We recieve issues as a markdown string. The markdown contains a section for + each field; the heading of a section is the "label", and the content that + follows it is the information the user submitted for that field. + + ``` + ### Field label + + Content user has submitted for that field. + ``` + + This function parses the markdown to create a dict of { label: content }, + then, using the issue template, transforms labels to IDs to create a + dictionary { id: content }. Since the IDs match arguments of the Repository + constructor, this dict is the "args" needed to create the Repository object. + + Since users can only submit strings, we map the string "_No response_" to + None and parse the "labels" field into a list. + Args: body_of_issue: body of an GitHub issue in markdown @@ -27,35 +67,18 @@ def parse_submission_issue(body_of_issue: str) -> Repository: issue_formatted = mdformat.text(body_of_issue) - sections = defaultdict( - None, [_clean_section(s) for s in issue_formatted.split("### ")[1:]] - ) + md_sections = issue_formatted.split("### ")[1:] + label_to_id = _get_label_to_id_map() + args = dict(_parse_section(s, label_to_id) for s in md_sections) - repo_name = sections["Github repo"].split("/")[-1] - - name = repo_name - url = sections["Github repo"] - description = sections["Description"] - contact_info = sections["Email"] - alternatives = sections["Alternatives"] - licence = sections["License"] - affiliations = sections["Affiliations"] - website = sections["Website"] - if website == "_No response_": - website = None - - labels = [l.strip() for l in sections["Labels"].split(",")] - if labels == ["_No response_"]: - labels = [] - - return Repository( - name=name, - url=url, - description=description, - licence=licence, - contact_info=contact_info, - alternatives=alternatives, - affiliations=affiliations, - labels=labels, - website=website, - ) + args = { + field_id: (None if content == "_No response_" else content) + for field_id, content in args.items() + } + + if args["labels"] is None: + args["labels"] = [] + else: + args["labels"] = [x.strip() for x in args["labels"].split(",")] + + return Repository(**args) diff --git a/requirements.txt b/requirements.txt index 6659a243b6..3db336dee5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ requests==2.31.0 coloredlogs==15.0.1 mdformat==0.7.17 toml==0.10.2 +PyYAML==6.0.1 diff --git a/tests/resources/issue.md b/tests/resources/issue.md index 28001a7a38..1fd6343fdc 100644 --- a/tests/resources/issue.md +++ b/tests/resources/issue.md @@ -1,3 +1,7 @@ +### Project name + +My awesome project + ### Github repo http://github.com/awesome/awesome diff --git a/tests/resources/issue_2.md b/tests/resources/issue_2.md index 2a7d5807d7..a446124092 100644 --- a/tests/resources/issue_2.md +++ b/tests/resources/issue_2.md @@ -1,3 +1,7 @@ +### Project name + +My awesome project + ### Github repo http://github.com/awesome/awesome diff --git a/tests/test_cli.py b/tests/test_cli.py index e40521d546..8f24ef5b00 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -28,12 +28,10 @@ class TestCli(TestCase): def setUp(self) -> None: self.path = Path(tempfile.mkdtemp()) - if not os.path.exists(self.path): - os.makedirs(self.path) + (self.path / "members").mkdir(parents=True, exist_ok=True) with open(self.path / "labels.json", "w") as file: file.write("{}") self.current_dir = os.path.dirname(os.path.abspath(__file__)) - print(self.current_dir) with open( "{}/resources/issue.md".format(self.current_dir), "r" ) as issue_body_file: @@ -46,7 +44,7 @@ def setUp(self) -> None: def tearDown(self) -> None: shutil.rmtree(self.path) - def test_parser_issue(self): + def test_add_member_from_issue(self): """Tests issue parsing function. Function: Cli -> parser_issue @@ -57,61 +55,45 @@ def test_parser_issue(self): # Issue 1 captured_output = io.StringIO() with redirect_stdout(captured_output): - CliCI.parser_issue(self.issue_body) + CliCI.add_member_from_issue(self.issue_body, resources_dir=self.path) output_value = captured_output.getvalue().split("\n") - - self.assertEqual(output_value[0], "SUBMISSION_NAME=awesome") - self.assertEqual( - output_value[1], - "SUBMISSION_REPO=http://github.com/awesome/awesome", - ) - self.assertEqual( - output_value[2], - "SUBMISSION_DESCRIPTION=An awesome repo for awesome project multiple" - " paragraphs", - ) - self.assertEqual(output_value[3], "SUBMISSION_LICENCE=Apache License 2.0") - self.assertEqual(output_value[4], "SUBMISSION_CONTACT=toto@gege.com") - self.assertEqual(output_value[5], "SUBMISSION_ALTERNATIVES=tititata") - self.assertEqual(output_value[6], "SUBMISSION_AFFILIATIONS=_No response_") - self.assertEqual( - output_value[7], - "SUBMISSION_LABELS=['tool', 'tutorial', 'paper implementation']", - ) - self.assertEqual( - output_value[8], - "SUBMISSION_WEBSITE=https://qiskit.org/ecosystem/", - ) + self.assertEqual(output_value[0], "SUBMISSION_NAME=My awesome project") + + retrieved_repos = DAO(self.path).get_all() + expected = { + "name": "My awesome project", + "url": "http://github.com/awesome/awesome", + "description": "An awesome repo for awesome project multiple paragraphs", + "contact_info": "toto@gege.com", + "alternatives": "tititata", + "licence": "Apache License 2.0", + "labels": ["tool", "tutorial", "paper implementation"], + "website": "https://qiskit.org/ecosystem/", + } + self.assertEqual(len(retrieved_repos), 1) + self.assertEqual(list(retrieved_repos)[0].to_dict(), expected) # Issue 2 captured_output = io.StringIO() with redirect_stdout(captured_output): - CliCI.parser_issue(self.issue_body_2) + CliCI.add_member_from_issue(self.issue_body_2, resources_dir=self.path) output_value = captured_output.getvalue().split("\n") - - self.assertEqual(output_value[0], "SUBMISSION_NAME=awesome") - self.assertEqual( - output_value[1], - "SUBMISSION_REPO=http://github.com/awesome/awesome", - ) - self.assertEqual( - output_value[2], - "SUBMISSION_DESCRIPTION=An awesome repo for awesome project", - ) - self.assertEqual(output_value[3], "SUBMISSION_LICENCE=Apache License 2.0") - self.assertEqual(output_value[4], "SUBMISSION_CONTACT=toto@gege.com") - self.assertEqual(output_value[5], "SUBMISSION_ALTERNATIVES=_No response_") - self.assertEqual(output_value[6], "SUBMISSION_AFFILIATIONS=Awesome Inc.") - self.assertEqual( - output_value[7], - "SUBMISSION_LABELS=[]", - ) - self.assertEqual( - output_value[8], - "SUBMISSION_WEBSITE=None", - ) + self.assertEqual(output_value[0], "SUBMISSION_NAME=My awesome project") + + retrieved_repos = DAO(self.path).get_all() + expected = { + "name": "My awesome project", + "url": "http://github.com/awesome/awesome", + "description": "An awesome repo for awesome project", + "contact_info": "toto@gege.com", + "licence": "Apache License 2.0", + "affiliations": "Awesome Inc.", + "labels": [], + } + self.assertEqual(len(retrieved_repos), 1) + self.assertEqual(list(retrieved_repos)[0].to_dict(), expected) def test_update_badges(self): """Tests creating badges.""" diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py index 343fdd4ad7..58554099bb 100644 --- a/tests/utils/test_utils.py +++ b/tests/utils/test_utils.py @@ -1,6 +1,11 @@ """Tests for manager.""" + import os +import dataclasses from unittest import TestCase +from pathlib import Path + +import yaml from ecosystem.models.repository import Repository from ecosystem.utils import parse_submission_issue @@ -27,7 +32,7 @@ def test_issue_parsing(self): parsed_result = parse_submission_issue(self.issue_body) self.assertTrue(isinstance(parsed_result, Repository)) - self.assertEqual(parsed_result.name, "awesome") + self.assertEqual(parsed_result.name, "My awesome project") self.assertEqual(parsed_result.url, "http://github.com/awesome/awesome") self.assertEqual( parsed_result.description, @@ -36,7 +41,27 @@ def test_issue_parsing(self): self.assertEqual(parsed_result.contact_info, "toto@gege.com") self.assertEqual(parsed_result.alternatives, "tititata") self.assertEqual(parsed_result.licence, "Apache License 2.0") - self.assertEqual(parsed_result.affiliations, "_No response_") + self.assertEqual(parsed_result.affiliations, None) self.assertEqual( parsed_result.labels, ["tool", "tutorial", "paper implementation"] ) + + def test_issue_template_matches_repository_model(self): + """Make sure IDs in the issue template match attributes of the Repository model.""" + issue_template = yaml.load( + Path(".github/ISSUE_TEMPLATE/submission.yml").read_text(), + Loader=yaml.SafeLoader, + ) + issue_ids = { + field["id"] + for field in issue_template["body"] + if field["type"] != "markdown" + } + + repo_fields = {attr.name for attr in dataclasses.fields(Repository)} + for issue_id in issue_ids: + self.assertIn( + issue_id, + repo_fields, + msg="\nA field exists in the issue template but not in the Repository class.", + )