From d5d9897d4c99539554696ebb7488f439b96eb42d Mon Sep 17 00:00:00 2001 From: Natalia Luzuriaga Date: Tue, 3 Jun 2025 12:50:02 -0700 Subject: [PATCH 01/11] Updated repositoryURL field and value if private Signed-off-by: Natalia Luzuriaga --- js/autoGenerateFields.js | 18 ++++++++++++------ schemas/schema.json | 4 ++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/js/autoGenerateFields.js b/js/autoGenerateFields.js index d018559..86bf652 100644 --- a/js/autoGenerateFields.js +++ b/js/autoGenerateFields.js @@ -208,7 +208,13 @@ async function preFillFields(repoData, languages) { // Updating URL if (repoData.html_url) { - form.getComponent('repositoryURL').setValue(repoData.html_url) + if (repoData.private) { + // Private repositories must have "private" as their repositoryURL value + form.getComponent('repositoryURL').setValue("private") + } + else { + form.getComponent('repositoryURL').setValue(repoData.html_url) + } } // Updating forks @@ -306,11 +312,11 @@ async function preFillFields(repoData, languages) { } // fields to potentially automate - // clones, but this is only tracked for every 14 days - // status, by checking if its public, we can assume its production and check if its archival - // laborHours, by running a script? this might be harder since we need SCC - // maturityModel, we could check to see if certain files / sections live within a repo and make a guess like that - // usageType, by assuming that if its public = openSource and if private = governmnetWideReuse + // clones, but this is only tracked for every 14 days + // status, by checking if its public, we can assume its production and check if its archival + // laborHours, by running a script? this might be harder since we need SCC + // maturityModel, we could check to see if certain files / sections live within a repo and make a guess like that + // usageType, by assuming that if its public = openSource and if private = governmnetWideReuse notificationSystem.success("Repository data loaded successfully!") diff --git a/schemas/schema.json b/schemas/schema.json index 989175a..2b6dfd1 100644 --- a/schemas/schema.json +++ b/schemas/schema.json @@ -114,7 +114,7 @@ "repositoryURL": { "type": "string", "format": "uri", - "description": "The URL of the public release repository for open source repositories. This field is not required for repositories that are only available as government-wide reuse or are closed (pursuant to one of the exemptions)." + "description": "The URL of the public release repository for open source repositories. This field is not required for repositories that are only available as government-wide reuse or are closed (pursuant to one of the exemptions). It can be listed as 'private' for repositories that are closed." }, "projectURL": { "type": "string", @@ -403,4 +403,4 @@ "maturityModelTier" ], "additionalProperties": false -} +} \ No newline at end of file From 7d82016b487dc0b658a9e1ad2826dfbccc2aee5e Mon Sep 17 00:00:00 2001 From: Natalia Luzuriaga Date: Thu, 3 Jul 2025 10:20:56 -0700 Subject: [PATCH 02/11] Add exemption section to form Signed-off-by: Natalia Luzuriaga --- css/styles.css | 27 ++-- index.html | 246 ++++++++++++++++++++++++----------- js/generateFormComponents.js | 36 +++-- 3 files changed, 212 insertions(+), 97 deletions(-) diff --git a/css/styles.css b/css/styles.css index ce97cee..8d29d31 100644 --- a/css/styles.css +++ b/css/styles.css @@ -6,27 +6,33 @@ textarea { margin-bottom: 10px; } -#auto-generation { +#auto-generation, +#exemptions { border-radius: 15px; padding: 20px; padding-bottom: 25px; margin-top: 20px; margin-bottom: 20px; background: lightgray; - width: 100%; - max-width: 800px; - box-sizing: border-box; + width: 100%; + max-width: 800px; + box-sizing: border-box; +} + +#exemptions { + max-width: 1300px; } -#github-url-form { +#github-url-form, +#exemptions-checklist { margin-top: 25px; width: 100%; } #repo-url { - width: 100%; - max-width: 750px; - box-sizing: border-box; + width: 100%; + max-width: 750px; + box-sizing: border-box; } #repo-url-button { @@ -51,11 +57,12 @@ textarea { } @keyframes slideDown { - from { + from { opacity: 0; transform: translateY(-10px); } - to { + + to { opacity: 1; transform: translateY(0); } diff --git a/index.html b/index.html index 80ca1d2..79c2be1 100644 --- a/index.html +++ b/index.html @@ -1,85 +1,183 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - + - - - - - + + + + + + + - - -
+ }) + .catch((error) => { + console.error("Error creating components:", error); + }); + + + + +
+ +
+

+
+
+ +
Security & Privacy
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
National Security and Intelligence
+
+ + +
+
+ + +
+
+ + +
+
Export and Regulatory Compliance
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + + +
+

+
+
+
+ +
+
- - -
-

-
-
-
- -
-
+
-
+
+ + + + + + +
-
- - - - - - -
+ - - + \ No newline at end of file diff --git a/js/generateFormComponents.js b/js/generateFormComponents.js index f708615..9e8fdfa 100644 --- a/js/generateFormComponents.js +++ b/js/generateFormComponents.js @@ -23,7 +23,7 @@ function transformArrayToOptions(arr) { } // Function that handles validation object needed for each form component -function determineValidation(fieldName, fieldObject, requiredArray){ +function determineValidation(fieldName, fieldObject, requiredArray) { return { "required": requiredArray.includes(fieldName) } @@ -207,7 +207,7 @@ function createComponent(fieldName, fieldObject, requiredArray) { description: fieldObject["description"], validate }; - case "container": + case "container": return { label: fieldName, hideLabel: false, @@ -238,7 +238,7 @@ function createComponent(fieldName, fieldObject, requiredArray) { input: true, components: [], validate - }; + }; default: break; } @@ -247,7 +247,16 @@ function createComponent(fieldName, fieldObject, requiredArray) { // Adds heading containing schema information function createFormHeading(title, description) { const container = document.getElementById('form-header'); - container.innerHTML = `

${title}

\n

${description}

`; + container.innerHTML = ` +

${title}

\n +

${description}

\n +

Complete the form below to create a code.json file for your project:

\n + `; +} + +function createExemptionsBox() { + const container = document.getElementById("exemptions-header") + container.innerHTML = `

Is my project exempted?

\n

Answer the series of questions below to determine if your project falls under the 4 exemption categories according to the SHARE IT Act.

` } function createAutoGenerationBox() { @@ -256,20 +265,20 @@ function createAutoGenerationBox() { } // Iterates through each json field and creates component array for Form.io -function createAllComponents(schema, prefix = ""){ +function createAllComponents(schema, prefix = "") { let components = []; if (schema.type === "object" && schema.properties) { const items = schema.properties.hasOwnProperty("items") ? schema.properties.items : schema.properties; - + let requiredArray = []; if (schema.hasOwnProperty("required")) { requiredArray = schema.required; } - for (const [key, value] of Object.entries(items)) { - + for (const [key, value] of Object.entries(items)) { + console.log("key at play:", key); const fullKey = prefix ? `${prefix}.${key}` : key; @@ -277,16 +286,16 @@ function createAllComponents(schema, prefix = ""){ if (fieldComponent.type === "container") { fieldComponent.components = createAllComponents(value, fullKey); - } + } else if (fieldComponent.type === "datagrid") { fieldComponent.components = createAllComponents(value.items, fullKey); } components.push(fieldComponent); - } - } + } + } - return components; + return components; } // Creates complete form based on input json schema @@ -298,6 +307,7 @@ async function createFormComponents() { console.log("JSON Data:", jsonData); createFormHeading(jsonData["title"], jsonData["description"]); + createExemptionsBox() createAutoGenerationBox() components = createAllComponents(jsonData); @@ -326,7 +336,7 @@ async function createFormComponents() { tableView: false, }); - + console.log(components); From 42766d164db0ba7519855ef46c79ff7f76b7c1c2 Mon Sep 17 00:00:00 2001 From: Natalia Luzuriaga Date: Tue, 22 Jul 2025 10:30:28 -0700 Subject: [PATCH 03/11] Added new schemas with redirects Signed-off-by: Natalia Luzuriaga --- 404.html | 23 +++ js/generateFormComponents.js | 5 +- schemas/{ => cms}/schema.json | 0 schemas/gov/schema.json | 295 ++++++++++++++++++++++++++++++++++ 4 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 404.html rename schemas/{ => cms}/schema.json (100%) create mode 100644 schemas/gov/schema.json diff --git a/404.html b/404.html new file mode 100644 index 0000000..994bea9 --- /dev/null +++ b/404.html @@ -0,0 +1,23 @@ + + + + + + + + + +

Redirecting...

+ + + \ No newline at end of file diff --git a/js/generateFormComponents.js b/js/generateFormComponents.js index 9e8fdfa..6e697d8 100644 --- a/js/generateFormComponents.js +++ b/js/generateFormComponents.js @@ -302,7 +302,10 @@ function createAllComponents(schema, prefix = "") { async function createFormComponents() { let components = []; - const filePath = "schemas/schema.json"; + // Fetching schema based on search params + const params = new URLSearchParams(window.location.search); + const page = params.get("page") || "gov"; + const filePath = `schemas/${page}/schema.json`; const jsonData = await retrieveFile(filePath); console.log("JSON Data:", jsonData); diff --git a/schemas/schema.json b/schemas/cms/schema.json similarity index 100% rename from schemas/schema.json rename to schemas/cms/schema.json diff --git a/schemas/gov/schema.json b/schemas/gov/schema.json new file mode 100644 index 0000000..d38caf2 --- /dev/null +++ b/schemas/gov/schema.json @@ -0,0 +1,295 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "code.json metadata", + "description": "A metadata standard for software repositories", + "type": "object", + "properties": { + "items": { + "name": { + "type": "string", + "description": "Name of the project or software" + }, + "description": { + "type": "string", + "description": "A short description of the project. It should be a single line containing a single sentence. Maximum 150 characters are allowed.", + "maxLength": 150 + }, + "longDescription": { + "type": "string", + "description": "Provide longer description of the software, between 150 and 10000 chars. It is meant to provide an overview of the capabilities of the software for a potential user.", + "minLength": 150, + "maxLength": 10000 + }, + "status": { + "type": "string", + "enum": [ + "Ideation", + "Development", + "Alpha", + "Beta", + "Release Candidate", + "Production", + "Archival" + ], + "description": "Development status of the project" + }, + "permissions": { + "type": "object", + "description": "An object containing description of the usage/restrictions regarding the release", + "properties": { + "licenses": { + "type": "array", + "description": "License(s) for the release", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "CC0-1.0", + "Apache-2.0", + "MIT", + "MPL-2.0", + "GPL-2.0-only", + "GPL-3.0-only", + "GPL-3.0-or-later", + "LGPL-2.1-only", + "LGPL-3.0-only", + "BSD-2-Clause", + "BSD-3-Clause", + "EPL-2.0", + "Other", + "None" + ], + "description": "An abbreviation for the name of the license" + }, + "URL": { + "type": "string", + "format": "uri", + "description": "The URL of the release license in the repository" + } + }, + "required": [ + "name", + "URL" + ] + } + }, + "usageType": { + "type": "string", + "description": "A list of enumerated values which describes the usage permissions for the release: (1) openSource: Open source; (2) governmentWideReuse: Government-wide reuse; (3) exemptByLaw: The sharing of the source code is restricted by law or regulation, including—but not limited to—patent or intellectual property law, the Export Asset Regulations, the International Traffic in Arms Regulation, and the Federal laws and regulations governing classified information; (4) exemptByNationalSecurity: The sharing of the source code would create an identifiable risk to the detriment of national security, confidentiality of Government information, or individual privacy; (5) exemptByAgencySystem: The sharing of the source code would create an identifiable risk to the stability, security, or integrity of the agency’s systems or personnel, (6) exemptByAgencyMission: The sharing of the source code would create an identifiable risk to agency mission, programs, or operations; (7) exemptByCIO: The CIO believes it is in the national interest to exempt sharing the source code; (8) exemptByPolicyDate: The release was created prior to the M-16-21 policy (August 8, 2016)", + "enum": [ + "openSource", + "governmentWideReuse", + "exemptByLaw", + "exemptByNationalSecurity", + "exemptByAgencySystem", + "exemptByAgencyMission", + "exemptByCIO", + "exemptByPolicyDate" + ], + "additionalProperties": false + }, + "exemptionText": { + "type": [ + "string", + "null" + ], + "description": "If an exemption is listed in the 'usageType' field, this field should include a one- or two- sentence justification for the exemption used." + } + }, + "additionalProperties": false, + "required": [ + "licenses", + "usageType" + ] + }, + "organization": { + "type": "string", + "description": "Organization responsible for the project", + "enum": [ + "Centers for Medicare & Medicaid Services" + ] + }, + "repositoryURL": { + "type": "string", + "format": "uri", + "description": "The URL of the public release repository for open source repositories. This field is not required for repositories that are only available as government-wide reuse or are closed (pursuant to one of the exemptions). It can be listed as 'private' for repositories that are closed." + }, + "repositoryVisibility": { + "type": "string", + "enum": [ + "public", + "private" + ], + "description": "Visibility of repository" + }, + "vcs": { + "type": "string", + "description": "Version control system used", + "enum": [ + "git", + "hg", + "svn", + "rcs", + "bzr" + ] + }, + "laborHours": { + "type": "number", + "description": "Labor hours invested in the project. Calculated using COCOMO measured by the SCC tool: https://github.com/boyter/scc?tab=readme-ov-file#cocomo" + }, + "reuseFrequency": { + "type": "object", + "description": "Measures frequency of code reuse in various forms. (e.g. forks, downloads, clones)", + "properties": { + "forks": { + "type": "integer" + }, + "clones": { + "type": "integer" + } + }, + "additionalProperties": true + }, + "platforms": { + "type": "array", + "description": "Platforms supported by the project", + "items": { + "type": "string", + "enum": [ + "web", + "windows", + "mac", + "linux", + "ios", + "android", + "other" + ] + } + }, + "categories": { + "type": "array", + "description": "Categories the project belongs to. Select from: https://yml.publiccode.tools/categories-list.html", + "items": { + "type": "string" + } + }, + "softwareType": { + "type": "string", + "description": "Type of software", + "enum": [ + "standalone/mobile", + "standalone/iot", + "standalone/desktop", + "standalone/web", + "standalone/backend", + "standalone/other", + "addon", + "library", + "configurationFiles" + ] + }, + "languages": { + "type": "array", + "description": "Programming languages that make up the codebase", + "items": { + "type": "string" + } + }, + "maintenance": { + "type": "string", + "description": "The dedicated staff that keeps the software up-to-date, if any", + "enum": [ + "internal", + "contract", + "community", + "none" + ] + }, + "contractNumber": { + "type": "string", + "description": "Contract number" + }, + "date": { + "type": "object", + "description": "A date object describing the release", + "properties": { + "created": { + "type": "string", + "format": "date-time", + "description": "Creation date of project." + }, + "lastModified": { + "type": "string", + "format": "date-time", + "description": "Date when the project was last modified" + }, + "metaDataLastUpdated": { + "type": "string", + "format": "date-time", + "description": "Date when metadata was last updated" + } + } + }, + "tags": { + "type": "array", + "description": "Topics and keywords associated with the project to improve search and discoverability", + "items": { + "type": "string" + } + }, + "contact": { + "type": "object", + "description": "Point of contact for the release", + "properties": { + "email": { + "type": "string", + "format": "email", + "description": "Email address of the point of contact" + }, + "name": { + "type": "string", + "description": "Name of the point of contact" + } + } + }, + "feedbackMechanisms": { + "type": "array", + "description": "Methods a repository receives feedback from the community. Default value is the URL to GitHub repository issues page.", + "items": { + "type": "string" + } + }, + "localisation": { + "type": "boolean", + "description": "Indicates if the project supports multiple languages" + } + } + }, + "required": [ + "name", + "description", + "longDescription", + "status", + "permissions", + "organization", + "repositoryURL", + "repositoryVisibility", + "vcs", + "laborHours", + "reuseFrequency", + "platforms", + "categories", + "softwareType", + "languages", + "maintenance", + "contractNumber", + "date", + "tags", + "contact", + "feedbackMechanisms", + "localisation" + ], + "additionalProperties": false +} \ No newline at end of file From 1dd70fc55ccec238f6c2719e1bc1a1cf3e6913ed Mon Sep 17 00:00:00 2001 From: Natalia Luzuriaga Date: Tue, 22 Jul 2025 10:39:28 -0700 Subject: [PATCH 04/11] Update filePath and linting Signed-off-by: Natalia Luzuriaga --- js/formDataToJson.js | 163 +++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 91 deletions(-) diff --git a/js/formDataToJson.js b/js/formDataToJson.js index 9b8f0a0..c4d939a 100644 --- a/js/formDataToJson.js +++ b/js/formDataToJson.js @@ -16,23 +16,23 @@ async function retrieveFile(filePath) { function isMultiSelect(obj) { for (const key in obj) { - if (typeof obj[key] !== 'boolean') { - return false; - } + if (typeof obj[key] !== 'boolean') { + return false; + } } return true; // Returns true if all values are booleans } // Convert from dictionary to array function getSelectedOptions(options) { - let selectedOptions = []; - - for (let key in options) { - if(options[key]) { - selectedOptions.push(key); - } - } - return selectedOptions; + let selectedOptions = []; + + for (let key in options) { + if (options[key]) { + selectedOptions.push(key); + } + } + return selectedOptions; } // Populates fields with form data @@ -46,7 +46,7 @@ function populateObject(data, schema) { let value = data[key]; // Adjusts value accordingly if multi-select field - if((typeof value === "object" && isMultiSelect(value))) { + if ((typeof value === "object" && isMultiSelect(value))) { value = getSelectedOptions(value); } @@ -57,7 +57,10 @@ function populateObject(data, schema) { } async function populateCodeJson(data) { - const filePath = "schemas/schema.json"; + // Fetching schema based on search params + const params = new URLSearchParams(window.location.search); + const page = params.get("page") || "gov"; + const filePath = `schemas/${page}/schema.json`; // Retrieves schema with fields in correct order const schema = await retrieveFile(filePath); @@ -87,37 +90,33 @@ async function createCodeJson(data) { } // Copies code.json to clipboard -async function copyToClipboard(event){ +async function copyToClipboard(event) { event.preventDefault(); var textArea = document.getElementById("json-result"); - textArea.select(); + textArea.select(); document.execCommand("copy") } const NEW_BRANCH = 'code-json-branch' + Math.random().toString(36).substring(2, 10); -function getOrgAndRepoArgsGitHub(url) -{ +function getOrgAndRepoArgsGitHub(url) { const pattern = /https:\/\/github\.com\/([^\/]+)\/([^\/]+)/; - const match = url.match(pattern); + const match = url.match(pattern); - if(match) - { + if (match) { const owner = match[1]; const repo = match[2]; - return {owner,repo}; + return { owner, repo }; } - else - { + else { throw new Error('Invalid URL!'); } } -async function createBranchOnProject(projectURL, token) -{ - const {owner, repo} = getOrgAndRepoArgsGitHub(projectURL); +async function createBranchOnProject(projectURL, token) { + const { owner, repo } = getOrgAndRepoArgsGitHub(projectURL); const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/main`, { @@ -130,41 +129,37 @@ async function createBranchOnProject(projectURL, token) const data = await response.json(); - if (response.ok) - { + if (response.ok) { const sha = data.object.sha; - + const createBranchApiUrl = `https://api.github.com/repos/${owner}/${repo}/git/refs`; // Create the new branch from the base branch const newBranchResponse = await fetch(createBranchApiUrl, { method: 'POST', headers: { - 'Content-Type': 'application/json', - 'Authorization': `token ${token}`, + 'Content-Type': 'application/json', + 'Authorization': `token ${token}`, }, body: JSON.stringify({ - ref: `refs/heads/${NEW_BRANCH}`, // Name of the new branch - sha: sha, // SHA of the base branch (main) + ref: `refs/heads/${NEW_BRANCH}`, // Name of the new branch + sha: sha, // SHA of the base branch (main) }), }); const newBranchData = await newBranchResponse.json(); - if ( newBranchResponse.ok ) - { + if (newBranchResponse.ok) { console.log('New branch created successfully: ', newBranchData); return true; } - else - { + else { console.error('Error creating new branch: ', newBranchData); alert("Failed to create branch on project! Error code: " + newBranchResponse.status + ". Please check API Key permissions and try again.") return false; } } - else - { + else { console.error('Error fetching base branch info:', data); alert('Error fetching base branch info:', data); return false; @@ -172,16 +167,15 @@ async function createBranchOnProject(projectURL, token) } -async function addFileToBranch(projectURL, token, codeJSONObj) -{ - const {owner, repo} = getOrgAndRepoArgsGitHub(projectURL); +async function addFileToBranch(projectURL, token, codeJSONObj) { + const { owner, repo } = getOrgAndRepoArgsGitHub(projectURL); const FILE_PATH = 'code.json' const createFileApiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${FILE_PATH}`; const encodedContent = btoa(codeJSONObj); console.log("Content: ", encodedContent); console.log("Branch: ", NEW_BRANCH); - const response = await fetch(createFileApiUrl, + const response = await fetch(createFileApiUrl, { method: 'PUT', headers: { @@ -203,24 +197,21 @@ async function addFileToBranch(projectURL, token, codeJSONObj) const data = await response.json() - if ( response.ok ) - { + if (response.ok) { console.log('File added successfully: ', data); return true; } - else - { + else { console.error('Error adding file: ', data); alert("Failed to add file on project! Error code: " + response.status + ". Please check API Key permissions and try again.") return false; } } -async function createPR(projectURL, token) -{ - const {owner, repo} = getOrgAndRepoArgsGitHub(projectURL); +async function createPR(projectURL, token) { + const { owner, repo } = getOrgAndRepoArgsGitHub(projectURL); const createPrApiUrl = `https://api.github.com/repos/${owner}/${repo}/pulls`; - const response = await fetch(createPrApiUrl, + const response = await fetch(createPrApiUrl, { method: 'POST', headers: { @@ -240,13 +231,11 @@ async function createPR(projectURL, token) const data = await response.json(); - if (response.ok) - { + if (response.ok) { console.log('Pull request created successfully: ', data); return true; } - else - { + else { console.error("Error creating PR!: ", data); alert("Failed to create PR on project! Error code: " + response.status + ". Please check API Key permissions and try again.") return false; @@ -254,50 +243,42 @@ async function createPR(projectURL, token) } // Creates PR on requested project -async function createProjectPR(event){ +async function createProjectPR(event) { event.preventDefault(); var textArea = document.getElementById("json-result");//Step 1 var codeJSONObj = JSON.parse(textArea.value) - - if('gh_api_key' in window) - { + + if ('gh_api_key' in window) { var apiKey = window.gh_api_key; - - if ('repositoryURL' in codeJSONObj) - { + + if ('repositoryURL' in codeJSONObj) { var prCreated = false; //Step 1 - const branchCreated = await createBranchOnProject(codeJSONObj.repositoryURL,apiKey); - if (branchCreated) - { + const branchCreated = await createBranchOnProject(codeJSONObj.repositoryURL, apiKey); + if (branchCreated) { const fileAdded = await addFileToBranch(codeJSONObj.repositoryURL, apiKey, textArea.value); - if (fileAdded) - { + if (fileAdded) { prCreated = await createPR(codeJSONObj.repositoryURL, apiKey); - if(prCreated) - { + if (prCreated) { console.log("PR successfully created!"); alert("PR has been created!"); } } } - else - { + else { console.error("Could not create branch on requested repository with the requested API key!"); alert("Could not create branch on requested repository with the requested API key!"); } } - else - { + else { console.error("No URL found!"); alert("No URL given for project! Please provide project URL in repositoryURL text box"); } - + } - else - { + else { console.error("No API key found!"); alert("No API Key in submitted data! Please provide an API key"); } @@ -328,27 +309,27 @@ async function emailFile(event) { const codeJson = document.getElementById("json-result").value const jsonObject = JSON.parse(codeJson); - - try { - const cleanData = {...jsonObject}; - delete cleanData.submit; - const jsonString = JSON.stringify(cleanData, null, 2); + try { + const cleanData = { ...jsonObject }; + delete cleanData.submit; + + const jsonString = JSON.stringify(cleanData, null, 2); - const subject = "Code.json generator Results"; - const body = `Hello,\n\nHere are the code.json results:\n\n${jsonString}\n\nThank you!`; + const subject = "Code.json generator Results"; + const body = `Hello,\n\nHere are the code.json results:\n\n${jsonString}\n\nThank you!`; - const recipients = ["opensource@cms.hhs.gov"]; + const recipients = ["opensource@cms.hhs.gov"]; - const mailtoLink = `mailto:${recipients}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`; + const mailtoLink = `mailto:${recipients}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`; - window.location.href = mailtoLink; + window.location.href = mailtoLink; - console.log("Email client opened"); - } catch { - console.error("Error preparing email:", error); - showNotificationModal("Error preparing email. Please try again or copy the data manually.", 'error'); - } + console.log("Email client opened"); + } catch { + console.error("Error preparing email:", error); + showNotificationModal("Error preparing email. Please try again or copy the data manually.", 'error'); + } } window.createCodeJson = createCodeJson; From fcb2b721c3f44150e630348ff0d70abfc068aea9 Mon Sep 17 00:00:00 2001 From: Natalia Luzuriaga Date: Tue, 22 Jul 2025 10:54:11 -0700 Subject: [PATCH 05/11] Update schemas Signed-off-by: Natalia Luzuriaga --- schemas/cms/schema.json | 34 +++++++++++++++++----------------- schemas/gov/schema.json | 14 +++++++------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/schemas/cms/schema.json b/schemas/cms/schema.json index 2b6dfd1..239abe2 100644 --- a/schemas/cms/schema.json +++ b/schemas/cms/schema.json @@ -23,13 +23,13 @@ "status": { "type": "string", "enum": [ - "Ideation", - "Development", - "Alpha", - "Beta", - "Release Candidate", - "Production", - "Archival" + "ideation", + "development", + "alpha", + "beta", + "releaseCandidate", + "production", + "archival" ], "description": "Development status of the project" }, @@ -304,9 +304,9 @@ "type": "string", "description": "Level of security categorization assigned to an information system under the Federal Information Security Modernization Act (FISMA): https://security.cms.gov/learn/federal-information-security-modernization-act-fisma", "enum": [ - "Low", - "Moderate", - "High" + "low", + "moderate", + "high" ] }, "group": { @@ -336,10 +336,10 @@ "items": { "type": "string", "enum": [ - "Policy", - "Operational", - "Medicare", - "Medicaid" + "policy", + "operational", + "medicare", + "medicaid" ] }, "description": "Healthcare-related subset" @@ -349,9 +349,9 @@ "items": { "type": "string", "enum": [ - "Providers", - "Patients", - "Government" + "providers", + "patients", + "government" ] }, "description": "Types of users who interact with the software" diff --git a/schemas/gov/schema.json b/schemas/gov/schema.json index d38caf2..8a4b5c4 100644 --- a/schemas/gov/schema.json +++ b/schemas/gov/schema.json @@ -23,13 +23,13 @@ "status": { "type": "string", "enum": [ - "Ideation", - "Development", - "Alpha", - "Beta", - "Release Candidate", - "Production", - "Archival" + "ideation", + "development", + "alpha", + "beta", + "releaseCandidate", + "production", + "archival" ], "description": "Development status of the project" }, From 9834850e187a50f51c2d03706264754278c99888 Mon Sep 17 00:00:00 2001 From: Natalia Luzuriaga Date: Tue, 22 Jul 2025 10:59:38 -0700 Subject: [PATCH 06/11] Update 404.html to redirect to page if exists Signed-off-by: Natalia Luzuriaga --- 404.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/404.html b/404.html index 994bea9..de1a8b8 100644 --- a/404.html +++ b/404.html @@ -7,17 +7,19 @@ // Extract the path from URL (e.g., /cms) and redirect (function () { const repoName = "codejson-generator"; // change if needed - let path = window.location.pathname.replace(`/${repoName}`, "").replace(/^\/+/, "").replace(/\/$/, ""); - const newUrl = path - ? `/${repoName ? repoName + "/" : ""}?page=${encodeURIComponent(path)}` - : `/${repoName ? repoName + "/" : ""}`; - window.location.replace(newUrl); + const pages = ["gov", "cms"]; + const path = window.location.pathname.replace(`/${repoName}`, "").replace(/^\/+/, "").replace(/\/$/, ""); + const redirectPage = pages.includes(path); + if (redirectPage) { + var newUrl = `/${repoName ? repoName + "/" : ""}?page=${encodeURIComponent(path)}`; + window.location.replace(newUrl); + } })(); -

Redirecting...

+

Page not found.

\ No newline at end of file From 9a344f26a6d74f41975b2aec6154920b3025fcf5 Mon Sep 17 00:00:00 2001 From: Natalia Luzuriaga Date: Tue, 22 Jul 2025 11:31:16 -0700 Subject: [PATCH 07/11] test redirectPage in 404.html Signed-off-by: Natalia Luzuriaga --- 404.html | 1 + 1 file changed, 1 insertion(+) diff --git a/404.html b/404.html index de1a8b8..95b8a04 100644 --- a/404.html +++ b/404.html @@ -11,6 +11,7 @@ const path = window.location.pathname.replace(`/${repoName}`, "").replace(/^\/+/, "").replace(/\/$/, ""); const redirectPage = pages.includes(path); if (redirectPage) { + console.log(redirectPage, "redirectPage") var newUrl = `/${repoName ? repoName + "/" : ""}?page=${encodeURIComponent(path)}`; window.location.replace(newUrl); } From fc60e285f9895a9d1c569b7dce6b5d1df4e0dbad Mon Sep 17 00:00:00 2001 From: Natalia Luzuriaga Date: Tue, 22 Jul 2025 11:33:27 -0700 Subject: [PATCH 08/11] Update 404.html Signed-off-by: Natalia Luzuriaga --- 404.html | 1 - 1 file changed, 1 deletion(-) diff --git a/404.html b/404.html index 95b8a04..de1a8b8 100644 --- a/404.html +++ b/404.html @@ -11,7 +11,6 @@ const path = window.location.pathname.replace(`/${repoName}`, "").replace(/^\/+/, "").replace(/\/$/, ""); const redirectPage = pages.includes(path); if (redirectPage) { - console.log(redirectPage, "redirectPage") var newUrl = `/${repoName ? repoName + "/" : ""}?page=${encodeURIComponent(path)}`; window.location.replace(newUrl); } From 5fa4f2039fece2e35d0fbd5e8a5bbceaf69faa50 Mon Sep 17 00:00:00 2001 From: Natalia Luzuriaga Date: Wed, 23 Jul 2025 11:27:02 -0700 Subject: [PATCH 09/11] Update schema.json --- schemas/gov/schema.json | 118 ++-------------------------------------- 1 file changed, 4 insertions(+), 114 deletions(-) diff --git a/schemas/gov/schema.json b/schemas/gov/schema.json index 8a4b5c4..e01857a 100644 --- a/schemas/gov/schema.json +++ b/schemas/gov/schema.json @@ -14,25 +14,6 @@ "description": "A short description of the project. It should be a single line containing a single sentence. Maximum 150 characters are allowed.", "maxLength": 150 }, - "longDescription": { - "type": "string", - "description": "Provide longer description of the software, between 150 and 10000 chars. It is meant to provide an overview of the capabilities of the software for a potential user.", - "minLength": 150, - "maxLength": 10000 - }, - "status": { - "type": "string", - "enum": [ - "ideation", - "development", - "alpha", - "beta", - "releaseCandidate", - "production", - "archival" - ], - "description": "Development status of the project" - }, "permissions": { "type": "object", "description": "An object containing description of the usage/restrictions regarding the release", @@ -104,13 +85,6 @@ "usageType" ] }, - "organization": { - "type": "string", - "description": "Organization responsible for the project", - "enum": [ - "Centers for Medicare & Medicaid Services" - ] - }, "repositoryURL": { "type": "string", "format": "uri", @@ -124,17 +98,6 @@ ], "description": "Visibility of repository" }, - "vcs": { - "type": "string", - "description": "Version control system used", - "enum": [ - "git", - "hg", - "svn", - "rcs", - "bzr" - ] - }, "laborHours": { "type": "number", "description": "Labor hours invested in the project. Calculated using COCOMO measured by the SCC tool: https://github.com/boyter/scc?tab=readme-ov-file#cocomo" @@ -152,51 +115,6 @@ }, "additionalProperties": true }, - "platforms": { - "type": "array", - "description": "Platforms supported by the project", - "items": { - "type": "string", - "enum": [ - "web", - "windows", - "mac", - "linux", - "ios", - "android", - "other" - ] - } - }, - "categories": { - "type": "array", - "description": "Categories the project belongs to. Select from: https://yml.publiccode.tools/categories-list.html", - "items": { - "type": "string" - } - }, - "softwareType": { - "type": "string", - "description": "Type of software", - "enum": [ - "standalone/mobile", - "standalone/iot", - "standalone/desktop", - "standalone/web", - "standalone/backend", - "standalone/other", - "addon", - "library", - "configurationFiles" - ] - }, - "languages": { - "type": "array", - "description": "Programming languages that make up the codebase", - "items": { - "type": "string" - } - }, "maintenance": { "type": "string", "description": "The dedicated staff that keeps the software up-to-date, if any", @@ -211,27 +129,6 @@ "type": "string", "description": "Contract number" }, - "date": { - "type": "object", - "description": "A date object describing the release", - "properties": { - "created": { - "type": "string", - "format": "date-time", - "description": "Creation date of project." - }, - "lastModified": { - "type": "string", - "format": "date-time", - "description": "Date when the project was last modified" - }, - "metaDataLastUpdated": { - "type": "string", - "format": "date-time", - "description": "Date when metadata was last updated" - } - } - }, "tags": { "type": "array", "description": "Topics and keywords associated with the project to improve search and discoverability", @@ -261,9 +158,9 @@ "type": "string" } }, - "localisation": { + "AIUseCaseInventory": { "type": "boolean", - "description": "Indicates if the project supports multiple languages" + "description": "Is the software included in the agency's AI use case inventory?" } } }, @@ -271,25 +168,18 @@ "name", "description", "longDescription", - "status", "permissions", - "organization", "repositoryURL", "repositoryVisibility", - "vcs", "laborHours", "reuseFrequency", - "platforms", - "categories", - "softwareType", "languages", "maintenance", "contractNumber", - "date", "tags", "contact", "feedbackMechanisms", - "localisation" + "AIUseCaseInventory" ], "additionalProperties": false -} \ No newline at end of file +} From 634d8296ef69da18c3dddc8fb4fbe36b88e16a77 Mon Sep 17 00:00:00 2001 From: Natalia Luzuriaga Date: Mon, 28 Jul 2025 11:23:17 -0700 Subject: [PATCH 10/11] Add form steps Signed-off-by: Natalia Luzuriaga --- css/styles.css | 20 ++++++++++++++++++++ index.html | 8 ++++++++ 2 files changed, 28 insertions(+) diff --git a/css/styles.css b/css/styles.css index 8d29d31..70a4bdb 100644 --- a/css/styles.css +++ b/css/styles.css @@ -56,6 +56,26 @@ textarea { padding-right: 20px; } +.step-header { + display: flex; + align-items: center; + gap: 0.5em; +} + +.step-number { + margin-top: 10px; + background-color: #005ea2; + color: white; + font-weight: bold; + width: 30px; + height: 30px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; +} + @keyframes slideDown { from { opacity: 0; diff --git a/index.html b/index.html index 79c2be1..8b65d8c 100644 --- a/index.html +++ b/index.html @@ -166,6 +166,14 @@
Export and Regulatory Compliance
+
+
+
3
+

Complete form and generate code.json file

+
+
+
+
From 1f2fc6078a084f7a7a6c0c6bd577fdc6d661e91b Mon Sep 17 00:00:00 2001 From: Natalia Luzuriaga Date: Mon, 28 Jul 2025 11:23:53 -0700 Subject: [PATCH 11/11] Update html with steps Signed-off-by: Natalia Luzuriaga --- js/generateFormComponents.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/generateFormComponents.js b/js/generateFormComponents.js index 6e697d8..2b08519 100644 --- a/js/generateFormComponents.js +++ b/js/generateFormComponents.js @@ -256,12 +256,12 @@ function createFormHeading(title, description) { function createExemptionsBox() { const container = document.getElementById("exemptions-header") - container.innerHTML = `

Is my project exempted?

\n

Answer the series of questions below to determine if your project falls under the 4 exemption categories according to the SHARE IT Act.

` + container.innerHTML = `
1

Is my project exempted from the SHARE IT Act?

\n

Answer the series of questions below to determine if your project falls under the 4 exemption categories according to the SHARE IT Act.

` } function createAutoGenerationBox() { const container = document.getElementById("auto-generation-header") - container.innerHTML = `

Auto Generate Fields

\n

Please enter your repositories GitHub URL in order to automatically pre-fill some of the fields in this form!

\n
This currently only works on public repositories
` + container.innerHTML = `
2

Auto Generate Fields

\n

Please enter your repositories GitHub URL in order to automatically pre-fill some of the fields in this form!

\n
This currently only works on public repositories
` } // Iterates through each json field and creates component array for Form.io