Skip to content

Commit

Permalink
#23 allow codemeta v3.0 import
Browse files Browse the repository at this point in the history
  • Loading branch information
hjonin committed Apr 5, 2024
1 parent c2d17bd commit 5ad0e7b
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 10 deletions.
68 changes: 68 additions & 0 deletions cypress/integration/basics.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ describe('JSON Import', function() {
it('works with expanded document version', function () {
cy.get('#codemetaText').then((elem) =>
elem.text(JSON.stringify({
"@context": "https://doi.org/10.5063/schema/codemeta-2.0",
"http://schema.org/name": [
{
"@value": "My Test Software"
Expand Down Expand Up @@ -264,4 +265,71 @@ describe('JSON Import', function() {
cy.get('#name').should('have.value', 'My Test Software');
});

it('works for new codemeta v3.0 terms', function() {
cy.get('#codemetaText').then((elem) =>
elem.text(JSON.stringify({
"@context": "https://w3id.org/codemeta/3.0",
"type": "SoftwareSourceCode",
"name": "My Test Software",
"continuousIntegration": "https://test-ci.org/my-software",
"isSourceCodeOf": "Bigger Application",
"review": {
"type": "Review",
"reviewAspect": "Some software aspect",
"reviewBody": "Some review"
}
}))
);
cy.get('#importCodemeta').click();

cy.get('#contIntegration').should('have.value', 'https://test-ci.org/my-software');
cy.get('#isSourceCodeOf').should('have.value', 'Bigger Application');
cy.get('#reviewAspect').should('have.value', 'Some software aspect');
cy.get('#reviewBody').should('have.value', 'Some review');
});

it('works for codemeta v2.0 terms in v3.0 version', function() {
cy.get('#codemetaText').then((elem) =>
elem.text(JSON.stringify({
"@context": "https://w3id.org/codemeta/3.0",
"type": "SoftwareSourceCode",
"name": "My Test Software",
"continuousIntegration": "https://test-ci.org/my-software",
"codemeta:contIntegration": {
"id": "https://test-ci.org/my-software"
},
}))
);
cy.get('#importCodemeta').click();

cy.get('#contIntegration').should('have.value', 'https://test-ci.org/my-software');
});

it('works for codemeta v3.0 terms in v2.0 version, and does not work for new terms', function() {
cy.get('#codemetaText').then((elem) =>
elem.text(JSON.stringify({
"@context": "https://doi.org/10.5063/schema/codemeta-2.0",
"type": "SoftwareSourceCode",
"name": "My Test Software",
"contIntegration": "https://test-ci.org/my-software",
"codemeta:continuousIntegration": {
"id": "https://test-ci.org/my-software"
},
"codemeta:isSourceCodeOf": {
"id": "Bigger Application"
},
"schema:review": {
"type": "schema:Review",
"schema:reviewAspect": "Some software aspect",
"schema:reviewBody": "Some review"
}
}))
);
cy.get('#importCodemeta').click();

cy.get('#contIntegration').should('have.value', 'https://test-ci.org/my-software');
cy.get('#isSourceCodeOf').should('have.value', '');
cy.get('#reviewAspect').should('have.value', '');
cy.get('#reviewBody').should('have.value', '');
});
});
145 changes: 144 additions & 1 deletion cypress/integration/persons.js
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,73 @@ describe('One role', function () {
});

it('can be imported', function () {
// TODO
cy.get('#codemetaText').then((elem) =>
elem.text(JSON.stringify({
"@context": "https://w3id.org/codemeta/3.0",
"type": "SoftwareSourceCode",
"name": "My Test Software",
"author": [
{
"type": "Role",
"schema:author": {
"type": "Person",
"givenName": "Jane"
},
"roleName": "Developer",
"startDate": "2024-03-04",
"endDate": "2024-04-03"
}
]
}))
);
cy.get('#importCodemeta').click();

cy.get('#author_1_givenName').should('have.value', 'Jane');
cy.get('#author_1_roleName_0').should('have.value', 'Developer');
cy.get('#author_1_startDate_0').should('have.value', '2024-03-04');
cy.get('#author_1_endDate_0').should('have.value', '2024-04-03');
});

it('and second one for the same author can be imported (and they are merged)', function () {
cy.get('#codemetaText').then((elem) =>
elem.text(JSON.stringify({
"@context": "https://w3id.org/codemeta/3.0",
"type": "SoftwareSourceCode",
"name": "My Test Software",
"author": [
{
"type": "Role",
"schema:author": {
"type": "Person",
"givenName": "Jane"
},
"roleName": "Maintainer",
"startDate": "2024-04-04",
"endDate": "2024-05-05"
},
{
"type": "Role",
"schema:author": {
"type": "Person",
"givenName": "Jane"
},
"roleName": "Developer",
"startDate": "2024-03-04",
"endDate": "2024-04-03"
}
]
}))
);
cy.get('#importCodemeta').click();

cy.get('#author_nb').should('have.value', '1');
cy.get('#author_1_givenName').should('have.value', 'Jane');
cy.get('#author_1_roleName_0').should('have.value', 'Maintainer');
cy.get('#author_1_startDate_0').should('have.value', '2024-04-04');
cy.get('#author_1_endDate_0').should('have.value', '2024-05-05');
cy.get('#author_1_roleName_1').should('have.value', 'Developer');
cy.get('#author_1_startDate_1').should('have.value', '2024-03-04');
cy.get('#author_1_endDate_1').should('have.value', '2024-04-03');
});
});

Expand Down Expand Up @@ -655,4 +721,81 @@ describe('Multiple authors', function () {
]
});
});

it('who both have roles can be imported', function () {
cy.get('#codemetaText').then((elem) =>
elem.text(JSON.stringify({
"@context": "https://w3id.org/codemeta/3.0",
"type": "SoftwareSourceCode",
"name": "My Test Software",
"author": [
{
"type": "Role",
"schema:author": {
"type": "Person",
"givenName": "Jane"
},
"roleName": "Developer",
"startDate": "2024-03-04",
"endDate": "2024-04-03"
},
{
"type": "Role",
"schema:author": {
"type": "Person",
"givenName": "Joe"
},
"roleName": "Maintainer",
"startDate": "2024-04-04",
"endDate": "2024-05-05"
}
]
}))
);
cy.get('#importCodemeta').click();

cy.get('#author_nb').should('have.value', '2');
cy.get('#author_1_givenName').should('have.value', 'Jane');
cy.get('#author_1_roleName_0').should('have.value', 'Developer');
cy.get('#author_1_startDate_0').should('have.value', '2024-03-04');
cy.get('#author_1_endDate_0').should('have.value', '2024-04-03');
cy.get('#author_2_givenName').should('have.value', 'Joe');
cy.get('#author_2_roleName_0').should('have.value', 'Maintainer');
cy.get('#author_2_startDate_0').should('have.value', '2024-04-04');
cy.get('#author_2_endDate_0').should('have.value', '2024-05-05');
});

it('whose one has a role and the other not can be imported', function () {
cy.get('#codemetaText').then((elem) =>
elem.text(JSON.stringify({
"@context": "https://w3id.org/codemeta/3.0",
"type": "SoftwareSourceCode",
"name": "My Test Software",
"author": [
{
"type": "Role",
"schema:author": {
"type": "Person",
"givenName": "Jane"
},
"roleName": "Developer",
"startDate": "2024-03-04",
"endDate": "2024-04-03"
},
{
"type": "Person",
"givenName": "Joe"
}
]
}))
);
cy.get('#importCodemeta').click();

cy.get('#author_nb').should('have.value', '2');
cy.get('#author_1_givenName').should('have.value', 'Joe');
cy.get('#author_2_givenName').should('have.value', 'Jane');
cy.get('#author_2_roleName_0').should('have.value', 'Developer');
cy.get('#author_2_startDate_0').should('have.value', '2024-03-04');
cy.get('#author_2_endDate_0').should('have.value', '2024-04-03');
});
});
68 changes: 60 additions & 8 deletions js/codemeta_generation.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ const directReviewCodemetaFields = [
'reviewBody'
];

const crossedCodemetaFields = {
const crossCodemetaFields = {
"contIntegration": ["contIntegration", "continuousIntegration"],
"embargoDate": ["embargoDate", "embargoEndDate"],
// "embargoDate": ["embargoDate", "embargoEndDate"], Not present in the form yet TODO ?
};

function generateShortOrg(fieldName) {
Expand Down Expand Up @@ -261,9 +261,9 @@ async function buildExpandedJson() {
doc["contributor"] = contributors;
}

for (const [key, values] of Object.entries(crossedCodemetaFields)) {
values.forEach(value => {
doc[value] = doc[key];
for (const [key, items] of Object.entries(crossCodemetaFields)) {
items.forEach(item => {
doc[item] = doc[key];
});
}
return await jsonld.expand(doc);
Expand Down Expand Up @@ -313,21 +313,64 @@ function importShortOrg(fieldName, doc) {
}
}

function importReview(doc) {
if (doc !== undefined) {
directReviewCodemetaFields.forEach(item => {
setIfDefined('#' + item, doc[item]);
});
}
}

function authorsEqual(author1, author2) {
// TODO should test more properties for equality?
return author1.givenName === author2.givenName
&& author1.email === author2.email;
}

function getSingleAuthorsFromRoles(docs) {
return docs.filter(doc => getDocumentType(doc) === "Role")
.map(doc => doc["schema:author"])
.reduce((authorSet, currentAuthor) => {
const foundAuthor = authorSet.find(author => authorsEqual(author, currentAuthor));
if (!foundAuthor) {
return authorSet.concat([currentAuthor]);
} else {
return authorSet;
}
}, []);
}

function importRoles(personPrefix, roles) {
roles.forEach(role => {
const roleId = addRole(`${personPrefix}`);
directRoleCodemetaFields.forEach(item => {
setIfDefined(`#${personPrefix}_${item}_${roleId}`, role[item]);
});
});
}

function importPersons(prefix, legend, docs) {
if (docs === undefined) {
return;
}

docs.forEach(function (doc, index) {
let allAuthorDocs = docs.filter(doc => getDocumentType(doc) === "Person");
allAuthorDocs = allAuthorDocs.concat(getSingleAuthorsFromRoles(docs));

allAuthorDocs.forEach(function (doc, index) {
var personId = addPerson(prefix, legend);

setIfDefined(`#${prefix}_${personId}_id`, getDocumentId(doc));
directPersonCodemetaFields.forEach(function (item, index) {
setIfDefined(`#${prefix}_${personId}_${item}`, doc[item]);
});

importShortOrg(`#${prefix}_${personId}_affiliation`, doc['affiliation'])
})
importShortOrg(`#${prefix}_${personId}_affiliation`, doc['affiliation']);

const roles = docs.filter(currentDoc => getDocumentType(currentDoc) === "Role")
.filter(currentDoc => authorsEqual(currentDoc["schema:author"], doc));
importRoles(`${prefix}_${personId}`, roles);
});
}

async function importCodemeta() {
Expand All @@ -351,6 +394,7 @@ async function importCodemeta() {
setIfDefined('#' + item, doc[item]);
});
importShortOrg('#funder', doc["funder"]);
importReview(doc["review"]);

// Import simple fields by joining on their separator
splittedCodemetaFields.forEach(function (item, index) {
Expand All @@ -365,6 +409,14 @@ async function importCodemeta() {
}
});

for (const [key, items] of Object.entries(crossCodemetaFields)) {
let value = "";
items.forEach(item => {
value = doc[item] || value;
});
setIfDefined(`#${key}`, value);
}

importPersons('author', 'Author', doc['author'])
importPersons('contributor', 'Contributor', doc['contributor'])
}
Expand Down
2 changes: 2 additions & 0 deletions js/dynamic_form.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ function addRole(personPrefix) {
.addEventListener('click', () => removeRole(personPrefix, roleIndex));

roleIndexNode.value = roleIndex + 1;

return roleIndex;
}

function removeRole(personPrefix, roleIndex) {
Expand Down
7 changes: 6 additions & 1 deletion js/validation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ function validateDocument(doc) {
}
// TODO: validate id/@id

if (doc["@context"] === undefined) {
setError("Missing context (required to determine import version).")
return false;
}

// TODO: check there is either type or @type but not both
var type = getDocumentType(doc);
if (type === undefined) {
Expand Down Expand Up @@ -89,6 +94,6 @@ async function parseAndValidateCodemeta(showPopup) {
}
}

doc = await jsonld.compact(parsed, CODEMETA_CONTEXTS["2.0"].url); // Only import codemeta v2.0 for now
doc = await jsonld.compact(parsed, parsed["@context"]);
return doc;
}

0 comments on commit 5ad0e7b

Please sign in to comment.