diff --git a/src/controllers/ReviewController.ts b/src/controllers/ReviewController.ts index bd6e5ff..a9a981c 100644 --- a/src/controllers/ReviewController.ts +++ b/src/controllers/ReviewController.ts @@ -68,21 +68,36 @@ export class ReviewController { return Object.prototype.hasOwnProperty.call(obj, 'persistentID'); } - // ... (rest of the code) if (errors.length > 0) { - console.log('Validation Errors: ', errors); - res.status(500).send( - errors.map((error: ValidationError) => { - let persistentID = 'missing id'; - if (error.target && typeof error.target === 'object' && hasPersistentID(error.target)) { - persistentID = error.target.persistentID; - } - return { - persistentID: persistentID, - constraints: error.constraints, - }; - }), + const processed_errors = errors.map((error: ValidationError) => { + let persistentID = 'missing id'; + if (error.target && typeof error.target === 'object' && hasPersistentID(error.target)) { + persistentID = error.target.persistentID; + } + const richConstraints = {}; + if (error.constraints) { + Object.entries(error.constraints).forEach(([key, message]) => { + const context = error.contexts ? error.contexts[key] : undefined; + const severity = context?.severity || 'error'; + richConstraints[key] = { + message: message, + severity: severity + }; + }); + } + return { + persistentID: persistentID, + property: error.property, + constraints: richConstraints, + }; + }); + console.log('Validation Errors: ', processed_errors); + return res.status(500).render('pages/review', + { + title: "Review Dashboard", + errors: processed_errors, + } ); } else { res.status(200).send(`successfully saved ${saves} entries`); diff --git a/src/db/entities/SLCItemCatalog.ts b/src/db/entities/SLCItemCatalog.ts index 1e207e6..dc01f3e 100644 --- a/src/db/entities/SLCItemCatalog.ts +++ b/src/db/entities/SLCItemCatalog.ts @@ -29,7 +29,7 @@ export class slc_item_catalog extends BaseEntity implements CatalogInterface { @IsNotEmpty() @IsString() @IsUrl() - @Reachable() + @Reachable({ context: { severity: 'warn'} }) iframe_url!: string; @Column() diff --git a/src/views/pages/review-dashboard.ejs b/src/views/pages/review-dashboard.ejs deleted file mode 100644 index 5a0fb36..0000000 --- a/src/views/pages/review-dashboard.ejs +++ /dev/null @@ -1,230 +0,0 @@ - - -<%- include('../partials/head') -%> -<%- include('../partials/header') -%> - -
-

Review Dashboard

-
-

Total Submissions: <%= totalSubmissions %>

-

Successful Verifications: <%= successfulVerifications %>

-

Submissions with Issues: <%= issues.length %>

-

Total Categorized Items: <%= categorizationResults.length %>

-

URLs Checked: <%= urlsChecked %>

-

Successful URLs: <%= successfulUrls %>

-

Unsuccessful URLs: <%= unsuccessfulUrls %>

- -
- - - - <% if (categorizationResults.length > 0) { %> -
Automatic Categorization Results
- -
- <% categorizationResults.forEach((result, index) => { %> -
-
-

Item: <%= result.item %>

-

Matched Class: <%= result.matchedClass %>

-

Status: <%= result.status %>

-
-
- <% }) %> -
- <% } %> - - <% if (issues.length > 0) { %> -
Entries with Issues
- -
- <% issues.forEach((issue, index) => { %> -
-
-

<%= issue.item.exercise_name %>

-
<%= issue.summary %>
- -
- -
-

Validation Errors:

-
    - <% issue.validationErrors.forEach(error => { %> -
  • <%= error.property %>: <%= error.constraints ? Object.values(error.constraints).join(', ') : 'Unknown error' %>
  • - <% }) %> -
-
- <% if (issue.error || issue.validationErrors.length > 0) { %> -
-

Recommendations:

-
    - <% if (issue.error) { %> -
  • Fix the URL or ensure the server is reachable: <%= issue.error %>
  • - <% } %> - <% issue.validationErrors.forEach(error => { %> -
  • Correct <%= error.property %>: <%= error.constraints ? Object.values(error.constraints).join(', ') : 'Refer to the guidelines' %>
  • - <% }) %> -
-
- <% } %> -
-
-
- <% }) %> -
-
-

Resources:

- -
- - -
- - -
- <% } else { %> -

No issues found!

- <% } %> - <% if (validationResults && validationResults.length > 0) { %> -
Individual Results
-
- - Download Validation Results JSON - -
-
- - - - - - - - - - - - - - <% validationResults.forEach(result => { %> - - - - - - <% const iframeMessage = result.iframeValidation?.message; %> - - - - - <% }) %> - -
Exercise NameURLMetadata IssuesURL ValidiFrame URLCategorization (keywords)LTI Launchable
<%= result.user %><%= result.item.url %> - <% if (result.metadataIssues === 'no issues') { %> -

Success

- <% } else if (result.metadataIssues) { %> -

failure

<%= result.metadataIssues %> - <% } else { %> - Pending - <% } %> -
- <% if (result.isUrlValid === true) { %> -

Success

- <% } else if (result.isUrlValid === false) { %> -

failure

- <% } else { %> - N/A - <% } %> -
- <% if (iframeMessage === 'Iframe validation failed' || iframeMessage == null) { %> -

Failure

- <% } else { %> -

Success

- <% } %> -
- <% if (result.categorizationResults?.startsWith('Success')) { %> -

Success

- <% } else if (result.categorizationResults) { %> -

failure

-

Categorization failed: No matching ontology class for keywords

- <% } else { %> -

failure

-

Categorization failed: No matching ontology class for keywords

- <% } %> -
- <% if (result.ltiValidationStatus === 'Launchable') { %> - Launchable - <% } else if (result.ltiValidationStatus === 'Not Launchable' || result.ltiValidationStatus === 'Validation Failed') { %> - <%= result.ltiValidationStatus %> - <% } else { %> - N/A - <% } %> -
-
- <% } else { %> -

No validation results available yet.

- <% } %> - - - - - - <%- include('../partials/footer') -%> - - - diff --git a/src/views/pages/review.ejs b/src/views/pages/review.ejs new file mode 100644 index 0000000..ce9c621 --- /dev/null +++ b/src/views/pages/review.ejs @@ -0,0 +1,30 @@ + + +<%- include('../partials/head') -%> +<%- include('../partials/header') -%> + + +
+

Validation Results

+ +
+ + <%- include('../partials/footer') -%> + + \ No newline at end of file