diff --git a/src/main/java/org/tdl/vireo/cli/Cli.java b/src/main/java/org/tdl/vireo/cli/Cli.java index 837ff589c..bb2d41ddd 100644 --- a/src/main/java/org/tdl/vireo/cli/Cli.java +++ b/src/main/java/org/tdl/vireo/cli/Cli.java @@ -129,13 +129,15 @@ public void run(String... args) throws Exception { Random random = new Random(); long idOffset = cliService.countUsers(); User helpfulHarry = null; + boolean hasSubmissionTypes = false; if (expansive) { helpfulHarry = cliService.createHelpfulHarry(idOffset++, CliService.EMAIL_DATE); + hasSubmissionTypes = cliService.hasSubmissionTypes(); } for (long i = itemsGenerated; i < num1 + itemsGenerated; i++) { - cliService.operateGenerate(expansive, num2, random, idOffset, helpfulHarry, i); + cliService.operateGenerate(expansive, num2, random, idOffset, helpfulHarry, hasSubmissionTypes, i); System.out.print("\r" + (i - itemsGenerated) + " of " + num1 + " generated..."); } diff --git a/src/main/java/org/tdl/vireo/controller/FieldValueController.java b/src/main/java/org/tdl/vireo/controller/FieldValueController.java new file mode 100644 index 000000000..44b4cbdcc --- /dev/null +++ b/src/main/java/org/tdl/vireo/controller/FieldValueController.java @@ -0,0 +1,40 @@ +package org.tdl.vireo.controller; + +import static edu.tamu.weaver.response.ApiStatus.SUCCESS; + +import edu.tamu.weaver.response.ApiResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.tdl.vireo.model.FieldPredicate; +import org.tdl.vireo.model.repo.FieldPredicateRepo; +import org.tdl.vireo.model.repo.FieldValueRepo; + +/** + * Controller in which to manage controlled vocabulary. + * + */ +@RestController +@RequestMapping("/settings/field-values") +public class FieldValueController { + + @Autowired + private FieldValueRepo fieldValueRepo; + + /** + * Endpoint to request a field predicate by value. + * + * @param value The Field Predicate value (not the Field Value). + * + * @return ApiResponse with all matching field values. + */ + @GetMapping("/predicate/{value}") + @PreAuthorize("hasRole('STUDENT')") + public ApiResponse getFieldValuesByPredicateValue(@PathVariable String value) { + return new ApiResponse(SUCCESS, fieldValueRepo.getAllValuesByFieldPredicateValue("submission_type")); + } + +} diff --git a/src/main/java/org/tdl/vireo/model/repo/FieldValueRepo.java b/src/main/java/org/tdl/vireo/model/repo/FieldValueRepo.java index df02a82fa..6a3980ab7 100644 --- a/src/main/java/org/tdl/vireo/model/repo/FieldValueRepo.java +++ b/src/main/java/org/tdl/vireo/model/repo/FieldValueRepo.java @@ -1,10 +1,9 @@ package org.tdl.vireo.model.repo; +import edu.tamu.weaver.data.model.repo.WeaverRepo; import org.tdl.vireo.model.FieldValue; import org.tdl.vireo.model.repo.custom.FieldValueRepoCustom; -import edu.tamu.weaver.data.model.repo.WeaverRepo; - public interface FieldValueRepo extends WeaverRepo, FieldValueRepoCustom { } diff --git a/src/main/java/org/tdl/vireo/model/repo/custom/FieldValueRepoCustom.java b/src/main/java/org/tdl/vireo/model/repo/custom/FieldValueRepoCustom.java index 0dedd4f72..fef20fdf6 100644 --- a/src/main/java/org/tdl/vireo/model/repo/custom/FieldValueRepoCustom.java +++ b/src/main/java/org/tdl/vireo/model/repo/custom/FieldValueRepoCustom.java @@ -1,5 +1,6 @@ package org.tdl.vireo.model.repo.custom; +import java.util.List; import org.tdl.vireo.model.FieldPredicate; import org.tdl.vireo.model.FieldValue; @@ -7,4 +8,6 @@ public interface FieldValueRepoCustom { public FieldValue create(FieldPredicate fieldPredicate); + public List getAllValuesByFieldPredicateValue(String fieldPredicateValue); + } diff --git a/src/main/java/org/tdl/vireo/model/repo/impl/FieldValueRepoImpl.java b/src/main/java/org/tdl/vireo/model/repo/impl/FieldValueRepoImpl.java index 50db20bd2..972e7bc8b 100644 --- a/src/main/java/org/tdl/vireo/model/repo/impl/FieldValueRepoImpl.java +++ b/src/main/java/org/tdl/vireo/model/repo/impl/FieldValueRepoImpl.java @@ -1,18 +1,30 @@ package org.tdl.vireo.model.repo.impl; +import edu.tamu.weaver.data.model.repo.impl.AbstractWeaverRepoImpl; +import java.util.ArrayList; +import java.util.List; +import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; import org.tdl.vireo.model.FieldPredicate; import org.tdl.vireo.model.FieldValue; import org.tdl.vireo.model.repo.FieldValueRepo; import org.tdl.vireo.model.repo.custom.FieldValueRepoCustom; -import edu.tamu.weaver.data.model.repo.impl.AbstractWeaverRepoImpl; - public class FieldValueRepoImpl extends AbstractWeaverRepoImpl implements FieldValueRepoCustom { + final static String VALUES_BY_PREDICATE = "SELECT DISTINCT fv.value AS value FROM field_value fv WHERE fv.field_predicate_id IN " + + "(SELECT fp.id FROM field_predicate fp WHERE fp.value = ?) ORDER BY fv.value ASC"; + @Autowired private FieldValueRepo fieldValueRepo; + private JdbcTemplate jdbcTemplate; + + public FieldValueRepoImpl(DataSource dataSource) { + jdbcTemplate = new JdbcTemplate(dataSource); + } + @Override public FieldValue create(FieldPredicate fieldPredicate) { return fieldValueRepo.save(new FieldValue(fieldPredicate)); @@ -23,4 +35,14 @@ protected String getChannel() { return "/channel/field-value"; } + @Override + public List getAllValuesByFieldPredicateValue(String fieldPredicateValue) { + List list = new ArrayList<>(); + jdbcTemplate.queryForList(VALUES_BY_PREDICATE, fieldPredicateValue).forEach(row -> { + list.add((String) row.get("value")); + }); + + return list; + } + } diff --git a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java index 334ba06d6..7f5687dd1 100644 --- a/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java +++ b/src/main/java/org/tdl/vireo/model/repo/impl/SubmissionRepoImpl.java @@ -750,7 +750,9 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { // Note that the OR query is used inside the column, represented by both default_embargos and proquest_embargos. boolean hasNone = false; for (String filterString : submissionListColumn.getFilters()) { - sqlBuilder.append(" value = '").append(escapeString(filterString, false)).append("' OR"); + if (filterString != null) { + sqlBuilder.append(" value = '").append(escapeString(filterString, false, true)).append("' OR"); + } if ("None".equals(filterString)) { hasNone = true; @@ -769,6 +771,36 @@ public int compare(SubmissionListColumn svc1, SubmissionListColumn svc2) { break; + case "submissionTypes.name": + // This is not a select column but is instead only a custom filter. + if (submissionListColumn.getFilters().size() > 0) { + sqlBuilder = new StringBuilder(); + sqlBuilder.append("s.id IN (SELECT submission_id FROM submission_field_values WHERE field_values_id IN (select id FROM field_value WHERE field_predicate_id IN (SELECT id FROM field_predicate WHERE value = 'submission_type') and ("); + + // Note that the OR query is used inside the column, represented by submission_type. + boolean hasNone = false; + for (String filterString : submissionListColumn.getFilters()) { + if (filterString != null) { + sqlBuilder.append(" value = '").append(escapeString(filterString, false, true)).append("' OR"); + } + + if ("None".equals(filterString)) { + hasNone = true; + } + } + sqlBuilder.setLength(sqlBuilder.length() - 3); + sqlBuilder.append(")))"); + + if (hasNone) { + sqlBuilder.append(" OR s.id NOT IN (SELECT submission_id FROM submission_field_values WHERE field_values_id IN (SELECT id FROM field_value WHERE field_predicate_id IN (SELECT id FROM field_predicate WHERE value = 'submission_type')))"); + } + + sqlWhereBuilderList.add(sqlBuilder); + getFromBuildersMap(sqlCountWhereFilterBuilders, "submissionTypes.name").add(sqlBuilder); + } + + break; + case "lastEvent": sqlBuilder = new StringBuilder() .append("\nLEFT JOIN") @@ -1153,7 +1185,7 @@ private ArrayList getFromBuildersMap(Map 0; + } + + public void operateGenerate(boolean expansive, int maxActionLogs, Random random, long idOffset, User helpfulHarry, boolean hasSubmissionTypes, long i) throws OrganizationDoesNotAcceptSubmissionsException { Calendar now = Calendar.getInstance(); User submitter = userRepo.create("bob" + EMAIL_DATE.format(now.getTime()) + (idOffset + i + 1) + "@boring.bob", "bob", "boring " + (idOffset + i + 1), Role.ROLE_STUDENT); Credentials credentials = new Credentials(); @@ -129,6 +137,7 @@ public void operateGenerate(boolean expansive, int maxActionLogs, Random random, final List departmentsVW = new ArrayList<>(); final List schoolsVW = new ArrayList<>(); final List majorsVW = new ArrayList<>(); + final List submissionTypeValues = new ArrayList<>(); Organization org = orgs.get(getRandomNumber(organizationRepo.findAll().size())); SubmissionStatus state = statuses.get(0); setAcceptSubmissions(org); @@ -147,6 +156,12 @@ public void operateGenerate(boolean expansive, int maxActionLogs, Random random, } }); + if (hasSubmissionTypes) { + fieldValueRepo.getAllValuesByFieldPredicateValue("submission_type").forEach((String value) -> { + submissionTypeValues.add(value); + }); + } + // Status is chosen completely randomly for every option available when expansive is enabled. if (expansive) { state = statuses.get(random.nextInt(statuses.size())); @@ -292,6 +307,17 @@ public void operateGenerate(boolean expansive, int maxActionLogs, Random random, val.setValue("test " + pred.getValue() + " " + i); val.setContacts(Arrays.asList(new String[] { "test" + pred.getValue() + i + AT_ADDRESS })); sub.addFieldValue(val); + } else if (pred.getValue().equalsIgnoreCase("submission_type")) { + String value = null; + if (submissionTypeValues.size() > 0) { + value = submissionTypeValues.get(getRandomNumber(submissionTypeValues.size())); + } + + if (value == null) { + val.setValue("test " + pred.getValue() + " " + getRandomNumber(10)); + } else { + val.setValue(value); + } } else { val.setValue("test " + pred.getValue() + " " + i); } diff --git a/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json b/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json index 1a892acab..b81bf84e0 100644 --- a/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json +++ b/src/main/resources/submission_list_columns/SYSTEM_Default_Submission_List_Columns.json @@ -209,5 +209,17 @@ "inputType": { "name": "INPUT_TEXT" } + }, + { + "title": "Submission Type (List)", + "sort": "NONE", + "valuePath": [ + "submissionTypes", + "name" + ], + "status": null, + "inputType": { + "name": "INPUT_TEXT" + } } ] diff --git a/src/main/webapp/app/controllers/submission/submissionListController.js b/src/main/webapp/app/controllers/submission/submissionListController.js index 917510c10..a1b9860bc 100644 --- a/src/main/webapp/app/controllers/submission/submissionListController.js +++ b/src/main/webapp/app/controllers/submission/submissionListController.js @@ -1,4 +1,4 @@ -vireo.controller("SubmissionListController", function (NgTableParams, $controller, $filter, $location, $q, $scope, ControlledVocabularyRepo, CustomActionDefinitionRepo, DepositLocationRepo, DocumentTypeRepo, EmailRecipient, EmailRecipientType, EmailTemplateRepo, EmbargoRepo, FieldPredicateRepo, ManagerFilterColumnRepo, ManagerSubmissionListColumnRepo, NamedSearchFilterGroup, OrganizationRepo, OrganizationCategoryRepo, PackagerRepo, SavedFilter, SavedFilterRepo, SidebarService, SubmissionListColumnRepo, SubmissionRepo, SubmissionStatusRepo, UserRepo, UserSettings, WsApi) { +vireo.controller("SubmissionListController", function (NgTableParams, $controller, $filter, $location, $q, $scope, ControlledVocabularyRepo, CustomActionDefinitionRepo, DepositLocationRepo, DocumentTypeRepo, EmailRecipient, EmailRecipientType, EmailTemplateRepo, EmbargoRepo, FieldPredicateRepo, FieldValueRepo, ManagerFilterColumnRepo, ManagerSubmissionListColumnRepo, NamedSearchFilterGroup, OrganizationRepo, OrganizationCategoryRepo, PackagerRepo, SavedFilter, SavedFilterRepo, SidebarService, SubmissionListColumnRepo, SubmissionRepo, SubmissionStatusRepo, UserRepo, UserSettings, WsApi) { angular.extend(this, $controller('AbstractController', { $scope: $scope @@ -29,6 +29,10 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle $scope.fieldPredicates = FieldPredicateRepo.getAll(); + $scope.simplifyTitle = function (str) { + return str.replaceAll(/[()\s]/gi, ''); + }; + var userSettings = new UserSettings(); var rowFilterTitle = "Exclude"; @@ -146,12 +150,27 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle }; var processUpdate = function (reloadList) { - var managerFilterColumns = ManagerFilterColumnRepo.getAll().filter(function excludeSearchBox(slc) { - return slc.title !== 'Search Box'; - }); - var submissionListColumns = SubmissionListColumnRepo.getAll().filter(function excludeSearchBox(slc) { - return slc.title !== 'Search Box'; - }); + var allManagerFilters = ManagerFilterColumnRepo.getAll(); + var allSubmissionListFilters = SubmissionListColumnRepo.getAll(); + var managerFilterColumns = []; + var submissionListColumns = []; + var submissionListColumnsForManage = []; + + if (!!allManagerFilters) { + managerFilterColumns = allManagerFilters.filter(function excludeSearchBox(slc) { + return slc.title !== 'Search Box'; + }); + } + + if (!!allSubmissionListFilters) { + submissionListColumns = allSubmissionListFilters.filter(function excludeCustomFilters(slc) { + return slc.title !== 'Search Box' && slc.title !== "Submission Type (List)"; + }); + + submissionListColumnsForManage = allSubmissionListFilters.filter(function excludeSearchBox(slc) { + return slc.title !== 'Search Box'; + }); + } $scope.userColumns = angular.fromJson(angular.toJson(ManagerSubmissionListColumnRepo.getAll())); @@ -172,7 +191,7 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle angular.extend(filterColumns, { userFilterColumns: managerFilterColumns, - inactiveFilterColumns: $filter('orderBy')($filter('exclude')(submissionListColumns, managerFilterColumns, 'title'), 'title') + inactiveFilterColumns: $filter('orderBy')($filter('exclude')(submissionListColumnsForManage, managerFilterColumns, 'title'), 'title') }); if (reloadList === true) { @@ -197,6 +216,7 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle var embargos = EmbargoRepo.getAll(); var packagers = PackagerRepo.getAll(); var controlledVocabularies = ControlledVocabularyRepo.getAll(); + var submissionTypeList = FieldValueRepo.findValuesByPredicateValue('submission_type'); var addBatchCommentEmail = function (message) { batchCommentEmail.adding = true; @@ -264,17 +284,16 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle return words; }; - var addFilter = function (column, gloss) { $scope.resetPagination(); - var filterValue = $scope.furtherFilterBy[column.title.split(" ").join("")]; - if (filterValue !== null) { + var filterValue = $scope.furtherFilterBy[$scope.simplifyTitle(column.title)]; + if (!!filterValue && typeof filterValue !== 'string') { filterValue = filterValue.toString(); } $scope.activeFilters.addFilter(column.title, filterValue, gloss, column.exactMatch).then(function () { - $scope.furtherFilterBy[column.title.split(" ").join("")] = ""; + $scope.furtherFilterBy[$scope.simplifyTitle(column.title)] = ""; query(); }); }; @@ -285,7 +304,7 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle }; var addDateFilter = function (column) { - var key = column.title.split(" ").join(""); + var key = $scope.simplifyTitle(column.title); var date1Value = $scope.furtherFilterBy[key].d1 var date2Value = $scope.furtherFilterBy[key].d2 ? $scope.furtherFilterBy[key].d2 : null; var dateGloss = date1Value; @@ -327,7 +346,7 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle } $scope.activeFilters.addFilter(column.title, date1Value, dateGloss, false).then(function () { - $scope.furtherFilterBy[column.title.split(" ").join("")] = ""; + $scope.furtherFilterBy[$scope.simplifyTitle(column.title)] = ""; query(); }); }; @@ -604,6 +623,7 @@ vireo.controller("SubmissionListController", function (NgTableParams, $controlle "organizationCategories": organizationCategories, "documentTypes": documentTypes, "embargos": embargos, + "submissionTypeList": submissionTypeList, "assignableUsers": assignableUsers, "defaultLimit": 3, "getTypeAheadByPredicateName": getTypeAheadByPredicateName, @@ -1068,10 +1088,12 @@ vireo.filter('exclude', function () { } if (prop) { exclude = exclude.map(function byProp(item) { + if (item === undefined || item == null) return undefined; return item[prop]; }); } return input.filter(function byExclude(item) { + if (item === undefined || item == null) return undefined; return exclude.indexOf(prop ? item[prop] : item) === -1; }); }; diff --git a/src/main/webapp/app/filters/uniqueSubmissionTypeList.js b/src/main/webapp/app/filters/uniqueSubmissionTypeList.js new file mode 100644 index 000000000..0f51414b6 --- /dev/null +++ b/src/main/webapp/app/filters/uniqueSubmissionTypeList.js @@ -0,0 +1,16 @@ +vireo.filter('uniqueSubmissionTypeList', function() { + return function(initialValues, isActive) { + var matched = []; + var newValues = []; + if (typeof initialValues === 'object') { + angular.forEach(initialValues, function(value) { + if (matched.includes(value)) return; + + matched.push(value); + newValues.push(value); + }); + } + + return newValues; + }; +}); diff --git a/src/main/webapp/app/model/submission.js b/src/main/webapp/app/model/submission.js index 695bac5ea..9d0da8453 100644 --- a/src/main/webapp/app/model/submission.js +++ b/src/main/webapp/app/model/submission.js @@ -358,6 +358,7 @@ var submissionModel = function ($filter, $q, ActionLog, FieldValue, FileService, resolve(); }); } + angular.extend(this.getMapping().validateFieldValue, { method: submission.id + "/validate-field-value/" + fieldProfile.id, data: fieldValue diff --git a/src/main/webapp/app/repo/fieldValueRepo.js b/src/main/webapp/app/repo/fieldValueRepo.js new file mode 100644 index 000000000..cecb28546 --- /dev/null +++ b/src/main/webapp/app/repo/fieldValueRepo.js @@ -0,0 +1,31 @@ +vireo.repo("FieldValueRepo", function FieldValueRepo(FieldValue, WsApi) { + + var fieldValueRepo = this; + + fieldValueRepo.findValuesByPredicateValue = function(value) { + var fieldValues = []; + + // FieldValue exists on the API Mapping but is under the Submission controller, which is not correct and so manually define the entire mapping inline here. + var mapping = { + 'endpoint': '/private/queue', + 'controller': 'settings/field-values', + 'method': 'predicate/' + value + }; + + WsApi.fetch(mapping).then(function(res) { + if (!!res && !!res.body) { + var resObj = angular.fromJson(res.body); + if (resObj.meta.status === 'SUCCESS') { + var values = resObj.payload['ArrayList']; + for (var i in values) { + fieldValues.push(values[i]); + } + } + } + }); + + return fieldValues; + }; + + return fieldValueRepo; +}); diff --git a/src/main/webapp/app/resources/styles/sass/app.scss b/src/main/webapp/app/resources/styles/sass/app.scss index f22e999bf..bf31d3965 100644 --- a/src/main/webapp/app/resources/styles/sass/app.scss +++ b/src/main/webapp/app/resources/styles/sass/app.scss @@ -525,6 +525,7 @@ sidebox ul.sidebox-list input { } .sidebox-body ul.sidebox-list.embargotype-list, +.sidebox-body ul.sidebox-list.submissiontype-list, .sidebox-body ul.sidebox-list.status-list { padding: 0px; } @@ -1562,6 +1563,7 @@ input[type=color]:focus { } .further-filter-by .embargotype-category, +.further-filter-by .submissiontype-category, .further-filter-by .status-category { background: #CCCCCC; display: inline-block; @@ -1575,6 +1577,7 @@ input[type=color]:focus { .further-filter-by .inactive-filters-sub-list, .further-filter-by .organization-by-category-sub-list, .further-filter-by .embargotype-list, +.further-filter-by .submissiontype-list, .further-filter-by .status-list { max-height: 0; overflow: hidden; @@ -1591,6 +1594,7 @@ input[type=color]:focus { .further-filter-by .inactive-filters-sub-list.expanded, .further-filter-by .organization-by-category-sub-list.expanded, .further-filter-by .embargotype-list.expanded, +.further-filter-by .submissiontype-list.expanded, .further-filter-by .status-list.expanded { max-height: 1000px; -webkit-transition: max-height 0.5s ease-in; diff --git a/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterBy.html b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterBy.html index a043b3211..73da63516 100644 --- a/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterBy.html +++ b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterBy.html @@ -31,6 +31,10 @@
+
+
+
+
diff --git a/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByEmbargoType.html b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByEmbargoType.html index 1af4109c1..36f0ce7c8 100644 --- a/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByEmbargoType.html +++ b/src/main/webapp/app/views/sideboxes/furtherFilterBy/furtherFilterByEmbargoType.html @@ -1,6 +1,6 @@