diff --git a/documentation/CHANGELOG.md b/documentation/CHANGELOG.md index 04df222108..f90c73369a 100644 --- a/documentation/CHANGELOG.md +++ b/documentation/CHANGELOG.md @@ -7,6 +7,14 @@ **Ticket Date Branch Version(current) Author Feature/Bug Description/Keywords** +6075 23.12.2024 dev 3.5 Andreas Feature Umstellung der Exports auf mehrstufiges Ladeverfahren + +6068 23.12.2024 dev 3.5 Andreas Feature Tests und Anpassungen der Titelverwaltung, hieran geknüpfte Bugfixes + +6048 23.12.2024 dev 3.5 Andreas Feature Umsetzung der neuen Regeln zur Titelverwaltung + +5937 23.12.2024 dev 3.5 Andreas Feature Titelverwaltung auf neue Struktur umgestellt, mehrstufiges Laden der Bestandstitel + 6127 19.12.2024 rc-3.4 3.4.16 Moe Bug Benennung Tab in der Lieferanten-Umfrage ändern 6118 19.12.2024 rc-3.4 3.4.16 Moe Bug Rechnungsstellungsabfrage: Button "öffentliche Kontakte anzeigen" umbennen diff --git a/grails-app/controllers/de/laser/MyInstitutionController.groovy b/grails-app/controllers/de/laser/MyInstitutionController.groovy index 1186e973f5..75af9ef464 100644 --- a/grails-app/controllers/de/laser/MyInstitutionController.groovy +++ b/grails-app/controllers/de/laser/MyInstitutionController.groovy @@ -1362,9 +1362,10 @@ class MyInstitutionController { }) def subscriptionsManagement() { Map result = myInstitutionControllerService.getResultGenerics(this, params) - + Profiler prf = new Profiler('subMgmt') + prf.setBenchmark('start loading data') params.tab = params.tab ?: 'generalProperties' - EhcacheWrapper filterCache = contextService.getUserCache("/subscriptionsManagement/subscriptionFilter/"), paginationCache = cacheService.getTTL1800Cache("/${params.controller}/subscriptionManagement/${params.tab}/${result.user.id}/pagination") + EhcacheWrapper filterCache = contextService.getUserCache("/subscriptionsManagement/subscriptionFilter/") Set filterFields = ['q', 'identifier', 'referenceYears', 'status', 'filterPropDef', 'filterProp', 'form', 'resource', 'subKinds', 'isPublicForApi', 'hasPerpetualAccess', 'hasPublishComponent', 'holdingSelection', 'subRunTime', 'subRunTimeMultiYear', 'subType', 'consortia'] filterFields.each { String subFilterKey -> if(params.containsKey('processOption')) { @@ -1377,7 +1378,6 @@ class MyInstitutionController { else filterCache.remove(subFilterKey) } } - result.selectionCache = paginationCache.checkedMap ?: [:] if(!(params.tab in ['notes', 'documents', 'properties'])){ //Important @@ -1388,7 +1388,7 @@ class MyInstitutionController { params.subTypes = [RDStore.SUBSCRIPTION_TYPE_LOCAL.id] } } - + prf.setBenchmark('get data') if(params.tab == 'documents' && params.processOption == 'newDoc') { def input_file = request.getFile("upload_file") if (input_file.size == 0) { @@ -1406,7 +1406,10 @@ class MyInstitutionController { }else{ result << managementService.subscriptionsManagement(this, params) } - + //at end because cache may get cleared after a process + EhcacheWrapper paginationCache = cacheService.getTTL1800Cache("/${params.controller}/subscriptionManagement/${params.tab}/${result.user.id}/pagination") + result.selectionCache = paginationCache.checkedMap ?: [:] + result.benchMark = prf.stopBenchmark() result } diff --git a/grails-app/controllers/de/laser/YodaController.groovy b/grails-app/controllers/de/laser/YodaController.groovy index a0998ef1fa..f8a896eb91 100644 --- a/grails-app/controllers/de/laser/YodaController.groovy +++ b/grails-app/controllers/de/laser/YodaController.groovy @@ -642,11 +642,13 @@ class YodaController { redirect(url: request.getHeader('referer')) } - @Deprecated @Secured(['ROLE_YODA']) - def getTIPPsWithoutGOKBId() { - log.debug("delete TIPPs without GOKb-ID") - yodaService.getTIPPsWithoutGOKBId() + def cleanupIssueEntitlements() { + log.debug("purge unnecessary titles ...") + executorService.execute ({ + yodaService.cleanupIssueEntitlements() + }) + redirect action: "systemThreads" } /** diff --git a/grails-app/controllers/de/laser/ajax/AjaxController.groovy b/grails-app/controllers/de/laser/ajax/AjaxController.groovy index c5b6c68577..7213b543a2 100644 --- a/grails-app/controllers/de/laser/ajax/AjaxController.groovy +++ b/grails-app/controllers/de/laser/ajax/AjaxController.groovy @@ -1,7 +1,6 @@ package de.laser.ajax -import de.laser.addressbook.Person -import de.laser.addressbook.PersonRole + import de.laser.auth.Role import de.laser.auth.User import de.laser.auth.UserRole @@ -49,6 +48,7 @@ import javax.servlet.ServletOutputStream import java.text.NumberFormat import java.text.SimpleDateFormat import java.time.Year +import java.util.concurrent.ExecutorService /** * This controller manages AJAX calls which result in object manipulation and / or do not deliver clearly either HTML or JSON. @@ -61,10 +61,12 @@ class AjaxController { ContextService contextService DashboardDueDatesService dashboardDueDatesService EscapeService escapeService + ExecutorService executorService FilterService filterService FormService formService GenericOIDService genericOIDService IdentifierService identifierService + PackageService packageService PropertyService propertyService SubscriptionControllerService subscriptionControllerService SubscriptionService subscriptionService @@ -238,6 +240,27 @@ class AjaxController { } } + if (target instanceof Subscription) { + if(params.name == 'holdingSelection') { + Org ctx = contextService.getOrg() + Subscription sub = (Subscription) target + Map configMap = [sub: sub, value: value] + subscriptionService.switchPackageHoldingInheritance(configMap) + List subChildIDs = sub.getDerivedSubscriptions().id + if(value == RDStore.SUBSCRIPTION_HOLDING_ENTIRE) { + executorService.execute({ + String threadName = 'PackageUnlink_'+sub.id + Thread.currentThread().setName(threadName) + sub.packages.each { SubscriptionPackage sp -> + if(!packageService.unlinkFromSubscription(sp.pkg, subChildIDs, ctx, false)){ + log.error('error on clearing issue entitlements when changing package holding selection') + } + } + }) + } + } + } + if (params.resultProp) { result = value[params.resultProp] } else { @@ -1179,31 +1202,12 @@ class AjaxController { } @Secured(['ROLE_USER']) - @Transactional def switchPackageHoldingInheritance() { if(formService.validateToken(params)) { - String prop = 'holdingSelection' Subscription sub = Subscription.get(params.id) RefdataValue value = RefdataValue.get(params.value) - sub.holdingSelection = value - sub.save() - if(value == RDStore.SUBSCRIPTION_HOLDING_ENTIRE) { - if(! AuditConfig.getConfig(sub, prop)) { - AuditConfig.addConfig(sub, prop) - - Subscription.findAllByInstanceOf(sub).each { Subscription m -> - m.setProperty(prop, sub.getProperty(prop)) - m.save() - } - } - } - else if(!value) { - AuditConfig.removeConfig(sub, prop) - Subscription.findAllByInstanceOf(sub).each { Subscription m -> - m.setProperty(prop, null) - m.save() - } - } + Map configMap = [sub: sub, value: value] + subscriptionService.switchPackageHoldingInheritance(configMap) } render([success: true] as JSON) } diff --git a/grails-app/controllers/de/laser/ajax/AjaxJsonController.groovy b/grails-app/controllers/de/laser/ajax/AjaxJsonController.groovy index f87630624d..97fb62f5f7 100644 --- a/grails-app/controllers/de/laser/ajax/AjaxJsonController.groovy +++ b/grails-app/controllers/de/laser/ajax/AjaxJsonController.groovy @@ -739,6 +739,21 @@ class AjaxJsonController { render controlledListService.getVendors(params) as JSON } + /** + * Retrieves a list of packages for dropdown display + * @return the result of {@link de.laser.ControlledListService#getPackages(grails.web.servlet.mvc.GrailsParameterMap)} + */ + @Secured(['ROLE_USER']) + def lookupPackages() { + if (params.ctx != "undefined") { + render controlledListService.getPackages(params) as JSON + } + else { + Map empty = [results: []] + render empty as JSON + } + } + /** * Retrieves a list of subscription packages for dropdown display * @return the result of {@link de.laser.ControlledListService#getSubscriptionPackages(grails.web.servlet.mvc.GrailsParameterMap)} diff --git a/grails-app/i18n/messages.properties b/grails-app/i18n/messages.properties index 6cd703aa95..47a7490fa1 100644 --- a/grails-app/i18n/messages.properties +++ b/grails-app/i18n/messages.properties @@ -2638,7 +2638,7 @@ subscriptionsManagement.unlinkInfo.costsExisting = "Costs were still existent fo subscriptionsManagement.unlinkInfo.blocked = There are still cost items linked to this package. Please delete them first. subscriptionsManagement.unlinkInfo.blockingSubscribersConsortia = Cost items concerning this package are linked to subscribers. Please delete those cost items first or unlink them from the package. subscriptionsManagement.unlinkInfo.blockingInheritanceSetting = Holding selection is being inherited -subscriptionsManagement.unlinkInfo.unlinkingInProgress = The package {0} is currently being unlinked +subscriptionsManagement.unlinkInfo.unlinkingInProgress = A bulk process is currently performed with {0}. subscriptionsManagement.properties = Properties subscriptionsManagement.propertySelected = Selected property diff --git a/grails-app/i18n/messages_de.properties b/grails-app/i18n/messages_de.properties index 5edd248fcd..11a550fd6e 100644 --- a/grails-app/i18n/messages_de.properties +++ b/grails-app/i18n/messages_de.properties @@ -2580,7 +2580,7 @@ subscriptionsManagement.unlinkInfo.costsExisting = "Für das Paket {0} von {1} w subscriptionsManagement.unlinkInfo.blocked = Es sind noch Kosten für das Paket anhängig. Bitte löschen Sie die Kosten zuerst. subscriptionsManagement.unlinkInfo.blockingSubscribersConsortia = Es sind noch Kosten für Pakete der Einrichtungslizenzen anhängig, die das Entknüpfen des Paketes unterbinden. Bitte löschen Sie diese Kosten zuerst oder heben Sie die Verknüpfung zum Paket auf. subscriptionsManagement.unlinkInfo.blockingInheritanceSetting = Paketzuschnitt wird geerbt -subscriptionsManagement.unlinkInfo.unlinkingInProgress = Das Paket {0} wird aktuell entknüpft +subscriptionsManagement.unlinkInfo.unlinkingInProgress = Mit dem Paket {0} findet aktuell ein Massenprozess statt. subscriptionsManagement.properties = Merkmale subscriptionsManagement.propertySelected = Ausgewähltes Merkmal diff --git a/grails-app/services/de/laser/ControlledListService.groovy b/grails-app/services/de/laser/ControlledListService.groovy index 930b21a6f7..1a65acacb6 100644 --- a/grails-app/services/de/laser/ControlledListService.groovy +++ b/grails-app/services/de/laser/ControlledListService.groovy @@ -411,6 +411,27 @@ class ControlledListService { result } + /** + * Retrieves a list of issue entitlements owned by the context institution matching given parameters + * @param params eventual request params + * @return a map containing a sorted list of issue entitlements, an empty one if no issue entitlements match the filter + */ + Map getPackages(GrailsParameterMap params) { + LinkedHashMap result = [results:[]] + String queryString = 'select p.id, p.name, (select count(*) from TitleInstancePackagePlatform tipp where tipp.pkg = p and tipp.status = :current) from Package p where p.packageStatus not in (:removed)' + LinkedHashMap filter = [current: RDStore.TIPP_STATUS_CURRENT, removed: [RDStore.PACKAGE_STATUS_DELETED, RDStore.PACKAGE_STATUS_REMOVED]] + //may be generalised later - here it is where to expand the query filter + if(params.query && params.query.length() > 0) { + filter.put('query', params.query) + queryString += " and genfunc_filter_matcher(p.name,:query) = true " + } + List rows = Package.executeQuery(queryString+" order by p.name asc",filter) + rows.each { row -> + result.results.add([name:"${row[1]} (${row[2]})",value:row[0]]) + } + result + } + /** * Retrieves a list of budget codes owned by the context institution matching given parameters * @param params eventual request params diff --git a/grails-app/services/de/laser/ExportClickMeService.groovy b/grails-app/services/de/laser/ExportClickMeService.groovy index 9adbd088dd..b5b06725bb 100644 --- a/grails-app/services/de/laser/ExportClickMeService.groovy +++ b/grails-app/services/de/laser/ExportClickMeService.groovy @@ -1856,6 +1856,11 @@ class ExportClickMeService { 'subscription.uuid' : [field: 'subscription.globalUID', label: 'Laser-UUID', message: null], ] ], + subscriptionIdentifiers: [ + label : 'Subscription Identifiers', + message: 'subscription.identifiers.label', + fields : [:] + ] ] } @@ -3747,6 +3752,9 @@ class ExportClickMeService { IdentifierNamespace.findAllByNsType(TitleInstancePackagePlatform.class.name, [sort: 'ns']).each { fields.issueEntitlementIdentifiers.fields << ["issueEntitlementIdentifiers.${it.id}":[field: null, label: it."${localizedName}" ?: it.ns]] } + IdentifierNamespace.findAllByNsType(Subscription.class.name, [sort: 'ns']).each { + fields.subscriptionIdentifiers.fields << ["subscriptionIdentifiers.${it.id}":[field: null, label: it."${localizedName}" ?: it.ns]] + } fields } @@ -3777,6 +3785,10 @@ class ExportClickMeService { exportFields.put("issueEntitlementIdentifiers."+it.id, [field: null, label: it."${localizedName}" ?: it.ns]) } + IdentifierNamespace.findAllByNsType(Subscription.class.name, [sort: 'ns']).each { + exportFields.put("subscriptionIdentifiers."+it.id, [field: null, label: it."${localizedName}" ?: it.ns]) + } + exportFields } @@ -6424,6 +6436,11 @@ class ExportClickMeService { queryCols << "create_cell('${format}', (select string_agg(id_value,';') from identifier where id_tipp_fk = tipp_id and id_ns_fk = :${argKey}), null) as ${argKey}" queryArgs.put(argKey, Long.parseLong(fieldKey.split("\\.")[1])) } + else if(fieldKey.startsWith('subscriptionIdentifiers.')) { + String argKey = "ns${i}" + queryCols << "create_cell('${format}', (select string_agg(id_value,';') from identifier where id_sub_fk = ie_subscription_fk and id_ns_fk = :${argKey}), null) as ${argKey}" + queryArgs.put(argKey, Long.parseLong(fieldKey.split("\\.")[1])) + } else if (fieldKey.contains('subscription.consortium')) { queryCols << "create_cell('${format}', (select org_name from org join org_role on org_id = or_org_fk where or_sub_fk = ie_subscription_fk and or_roletype_rv_fk = :consortium), null) as consName" queryArgs.consortium = RDStore.OR_SUBSCRIPTION_CONSORTIUM.id diff --git a/grails-app/services/de/laser/ManagementService.groovy b/grails-app/services/de/laser/ManagementService.groovy index 48730c04cb..39287a6fba 100644 --- a/grails-app/services/de/laser/ManagementService.groovy +++ b/grails-app/services/de/laser/ManagementService.groovy @@ -359,16 +359,16 @@ class ManagementService { } } else { - EhcacheWrapper paginationCache = cacheService.getTTL1800Cache("/${params.controller}/subscriptionManagement/linkPackage/${result.user.id}/pagination") + EhcacheWrapper paginationCache = cacheService.getTTL1800Cache("/${params.controller}/subscriptionManagement/linkPackages/${result.user.id}/pagination") List selectionCache = [] if(paginationCache.checkedMap) { selectionCache.addAll(paginationCache.checkedMap.values()) - paginationCache.remove('checkedMap') } else selectionCache.addAll(params.list('selectedSubs')) if(selectionCache) { subscriptions = Subscription.findAllByIdInList(selectionCache) } + paginationCache.remove('checkedMap') } List selectedPackageKeys = params.list("selectedPackages") Set pkgsToProcess = [] @@ -399,7 +399,10 @@ class ManagementService { subscriptionService.addToSubscriptionCurrentStock(selectedSub, result.subscription, pkg, params.processOption == 'linkwithIE') } else { - subscriptionService.addToSubscription(selectedSub, pkg, params.processOption == 'linkwithIE') + if(selectedSub.holdingSelection == RDStore.SUBSCRIPTION_HOLDING_ENTIRE) + subscriptionService.addToSubscription(selectedSub, pkg, true) + else + subscriptionService.addToSubscription(selectedSub, pkg, params.processOption == 'linkwithIE') } } } diff --git a/grails-app/services/de/laser/SubscriptionService.groovy b/grails-app/services/de/laser/SubscriptionService.groovy index f800b312ac..696b15d666 100644 --- a/grails-app/services/de/laser/SubscriptionService.groovy +++ b/grails-app/services/de/laser/SubscriptionService.groovy @@ -15,8 +15,6 @@ import de.laser.finance.PriceItem import de.laser.helper.* import de.laser.interfaces.CalculatedType import de.laser.properties.PropertyDefinition -import de.laser.properties.PropertyDefinitionGroup -import de.laser.properties.PropertyDefinitionGroupBinding import de.laser.properties.SubscriptionProperty import de.laser.remote.Wekb import de.laser.stats.Counter4Report @@ -900,19 +898,6 @@ class SubscriptionService { ies } - /** - * Gets the current issue entitlements for the given subscription - * @param subscription the subscription whose titles should be returned - * @return a sorted list of current issue entitlements - */ - List getCurrentIssueEntitlements(Subscription subscription) { - List ies = subscription? - IssueEntitlement.executeQuery("select ie from IssueEntitlement as ie where ie.subscription = :sub and ie.status = :cur order by ie.tipp.sortname", - [sub: subscription, cur: RDStore.TIPP_STATUS_CURRENT]) - : [] - ies - } - /** * Gets the current issue entitlements for the given subscription * @param subscription the subscription whose titles should be returned @@ -1343,74 +1328,29 @@ class SubscriptionService { } } - /** - * Builds the comparison map for the properties; inverting the relation subscription-properties to property-subscriptions - * @param subsToCompare the subscriptions whose property sets should be compared - * @param org the institution whose property definition groups should be considered - * @return the inverse property map - */ - Map regroupSubscriptionProperties(List subsToCompare, Org org) { - LinkedHashMap result = [groupedProperties:[:],orphanedProperties:[:],privateProperties:[:]] - subsToCompare.each{ sub -> - Map allPropDefGroups = sub.getCalculatedPropDefGroups(org) - allPropDefGroups.entrySet().each { propDefGroupWrapper -> - //group group level - //There are: global, local, member (consortium@subscriber) property *groups* and orphaned *properties* which is ONE group - String wrapperKey = propDefGroupWrapper.getKey() - if(wrapperKey.equals("orphanedProperties")) { - TreeMap orphanedProperties = result.orphanedProperties - orphanedProperties = comparisonService.buildComparisonTree(orphanedProperties,sub,propDefGroupWrapper.getValue()) - result.orphanedProperties = orphanedProperties - } - else { - LinkedHashMap groupedProperties = result.groupedProperties - //group level - //Each group may have different property groups - propDefGroupWrapper.getValue().each { propDefGroup -> - PropertyDefinitionGroup groupKey - PropertyDefinitionGroupBinding groupBinding - switch(wrapperKey) { - case "global": - groupKey = (PropertyDefinitionGroup) propDefGroup - if(groupKey.isVisible) - groupedProperties.put(groupKey,comparisonService.getGroupedPropertyTrees(groupedProperties,groupKey,null,sub)) - break - case "local": - try { - groupKey = (PropertyDefinitionGroup) propDefGroup.get(0) - groupBinding = (PropertyDefinitionGroupBinding) propDefGroup.get(1) - if(groupBinding.isVisible) { - groupedProperties.put(groupKey,comparisonService.getGroupedPropertyTrees(groupedProperties,groupKey,groupBinding,sub)) - } - } - catch (ClassCastException e) { - log.error("Erroneous values in calculated property definition group! Stack trace as follows:") - e.printStackTrace() - } - break - case "member": - try { - groupKey = (PropertyDefinitionGroup) propDefGroup.get(0) - groupBinding = (PropertyDefinitionGroupBinding) propDefGroup.get(1) - if(groupBinding.isVisible && groupBinding.isVisibleForConsortiaMembers) { - groupedProperties.put(groupKey,comparisonService.getGroupedPropertyTrees(groupedProperties,groupKey,groupBinding,sub)) - } - } - catch (ClassCastException e) { - log.error("Erroneous values in calculated property definition group! Stack trace as follows:") - e.printStackTrace() - } - break - } - } - result.groupedProperties = groupedProperties + void switchPackageHoldingInheritance(Map configMap) { + String prop = 'holdingSelection' + Subscription sub = configMap.sub + RefdataValue value = configMap.value + sub.holdingSelection = value + sub.save() + if(value == RDStore.SUBSCRIPTION_HOLDING_ENTIRE) { + if(! AuditConfig.getConfig(sub, prop)) { + AuditConfig.addConfig(sub, prop) + + Subscription.findAllByInstanceOf(sub).each { Subscription m -> + m.setProperty(prop, sub.getProperty(prop)) + m.save() } } - TreeMap privateProperties = result.privateProperties - privateProperties = comparisonService.buildComparisonTree(privateProperties,sub,sub.propertySet.findAll { it.type.tenant?.id == org.id }) - result.privateProperties = privateProperties } - result + else { + AuditConfig.removeConfig(sub, prop) + Subscription.findAllByInstanceOf(sub).each { Subscription m -> + m.setProperty(prop, null) + m.save() + } + } } /** diff --git a/grails-app/services/de/laser/YodaService.groovy b/grails-app/services/de/laser/YodaService.groovy index acda3e1d02..e6a8550caa 100644 --- a/grails-app/services/de/laser/YodaService.groovy +++ b/grails-app/services/de/laser/YodaService.groovy @@ -17,6 +17,7 @@ import de.laser.wekb.TitleInstancePackagePlatform import de.laser.wekb.Vendor import grails.gorm.transactions.Transactional import grails.plugin.springsecurity.SpringSecurityUtils +import groovy.sql.BatchingPreparedStatementWrapper import groovy.sql.GroovyRowResult import groovy.sql.Sql @@ -25,6 +26,7 @@ import io.micronaut.http.client.DefaultHttpClientConfiguration import io.micronaut.http.client.HttpClientConfiguration import org.hibernate.Session +import java.sql.Connection import java.time.Duration import java.util.concurrent.ExecutorService @@ -116,40 +118,50 @@ class YodaService { } /** - * Retrieves titles without we:kb ID - * @return a map containing faulty titles in the following structure: + * Retrieves {@link IssueEntitlement}s that should be deleted with trace + * @return a map containing issue entitlement traces in the following structure: *
    - *
  • titles with a remapping target
  • - *
  • titles with issue entitlements
  • - *
  • deletable entries
  • - *
  • titles which should receive a UUID
  • + *
  • issue entitlement ID
  • + *
  • {@link Package} we:kb ID
  • + *
  • subscription global UID
  • + *
  • {@link TitleInstancePackagePlatform} we:kb ID
  • *
*/ - Map getTIPPsWithoutGOKBId() { - List tippsWithoutGOKbID = TitleInstancePackagePlatform.findAllByGokbIdIsNullOrGokbIdLike(RDStore.GENERIC_NULL_VALUE.value) - List issueEntitlementsAffected = IssueEntitlement.executeQuery('select ie from IssueEntitlement ie where ie.tipp in :tipps',[tipps:tippsWithoutGOKbID]) - Map> ieTippMap = [:] - List> tippsWithAlternate = [] - Map toDelete = [:] - Set toUUIDfy = [] - tippsWithoutGOKbID.each { tipp -> - TitleInstancePackagePlatform altTIPP = TitleInstancePackagePlatform.executeQuery("select tipp from TitleInstancePackagePlatform tipp where tipp.pkg = :pkg and tipp.status = :current and tipp.gokbId != null",[pkg:tipp.pkg,current:RDStore.TIPP_STATUS_CURRENT])[0] - if(altTIPP) { - toDelete[tipp.id] = altTIPP.id - tippsWithAlternate << [tipp:tipp,altTIPP:altTIPP] + void cleanupIssueEntitlements() { + Set subsConcerned = Subscription.executeQuery('select s from Subscription s where s.holdingSelection = :entire and s.instanceOf != null', [entire: RDStore.SUBSCRIPTION_HOLDING_ENTIRE]) + Sql storageSql = GlobalService.obtainStorageSqlConnection(), sql = GlobalService.obtainSqlConnection() + Connection arrayConn = sql.getDataSource().getConnection() + //in order to distribute memory load + try { + subsConcerned.eachWithIndex { Subscription s, int si -> + Set> data = IssueEntitlement.executeQuery("select new map(ie.version as version, now() as dateCreated, now() as lastUpdated, ie.globalUID as oldGlobalUID, coalesce(ie.dateCreated, ie.lastUpdated, '1970-01-01') as oldDateCreated, coalesce(ie.lastUpdated, ie.dateCreated, '1970-01-01') as oldLastUpdated, '"+IssueEntitlement.class.name+"' as oldObjectType, ie.id as oldDatabaseId, tipp.name as oldName, pkg.gokbId as referencePackageWekbId, tipp.gokbId as referenceTitleWekbId, s.globalUID as referenceSubscriptionUID) from IssueEntitlement ie join ie.tipp tipp join tipp.pkg pkg join ie.subscription s where s = :subConcerned", [subConcerned: s]) + int offset = 0, step = 20000, total = data.size() + String query = "insert into deleted_object (do_version, do_old_date_created, do_old_last_updated, do_date_created, do_last_updated, do_old_object_type, do_old_database_id, do_old_global_uid, do_old_name, do_ref_package_wekb_id, do_ref_title_wekb_id, do_ref_subscription_uid) values (:version, :oldDateCreated, :oldLastUpdated, :dateCreated, :lastUpdated, :oldObjectType, :oldDatabaseId, :oldGlobalUID, :oldName, :referencePackageWekbId, :referenceTitleWekbId, :referenceSubscriptionUID)" + Set toDelete = [] + log.debug("now processing entry subscription ${si+1} out of ${subsConcerned.size()} for ${total} records") + if(data) { + storageSql.withBatch(step, query) { BatchingPreparedStatementWrapper stmt -> + for (offset; offset < total; offset++) { + stmt.addBatch(data[offset]) + if(offset % step == 0 && offset > 0) { + log.debug("reached ${offset} rows") + } + } + } + toDelete.addAll(data.oldDatabaseId) + toDelete.collate(65000).each { List part -> + sql.execute("delete from price_item where pi_ie_fk = any(:toDelete)", [toDelete: arrayConn.createArrayOf('bigint', part as Object[])]) + sql.execute("delete from permanent_title where pt_ie_fk = any(:toDelete)", [toDelete: arrayConn.createArrayOf('bigint', part as Object[])]) + sql.execute("delete from issue_entitlement_coverage where ic_ie_fk = any(:toDelete)", [toDelete: arrayConn.createArrayOf('bigint', part as Object[])]) + sql.execute("delete from issue_entitlement where ie_id = any(:toDelete)", [toDelete: arrayConn.createArrayOf('bigint', part as Object[])]) + } + } } - else toUUIDfy << tipp.id } - issueEntitlementsAffected.each { IssueEntitlement ie -> - if (ieTippMap.get(ie.tipp)) { - ieTippMap[ie.tipp] << ie - } else { - Set ies = new TreeSet() - ies.add(ie) - ieTippMap[ie.tipp] = ies - } + finally { + storageSql.close() + sql.close() } - [tipps: tippsWithAlternate, issueEntitlements: ieTippMap, toDelete: toDelete, toUUIDfy: toUUIDfy] } /** diff --git a/grails-app/services/de/laser/ctrl/SubscriptionControllerService.groovy b/grails-app/services/de/laser/ctrl/SubscriptionControllerService.groovy index 907a7520ae..63ce70dabb 100644 --- a/grails-app/services/de/laser/ctrl/SubscriptionControllerService.groovy +++ b/grails-app/services/de/laser/ctrl/SubscriptionControllerService.groovy @@ -2231,12 +2231,14 @@ class SubscriptionControllerService { executorService.execute({ String threadName = 'PackageUnlink_'+result.subscription.id Thread.currentThread().setName(threadName) - long start = System.currentTimeSeconds() + //long start = System.currentTimeSeconds() if(packageService.unlinkFromSubscription(result.package, subList.id, result.institution, unlinkPkg)){ result.message = messageSource.getMessage('subscription.details.unlink.successfully',null,locale) + /* if(System.currentTimeSeconds()-start >= GlobalService.LONG_PROCESS_LIMBO) { globalService.notifyBackgroundProcessFinish(result.user.id, threadName, messageSource.getMessage('subscription.details.unlink.thread.completed', [result.subscription.name] as Object[], locale)) } + */ }else { result.error = messageSource.getMessage('subscription.details.unlink.notSuccessfully',null,locale) } diff --git a/grails-app/views/license/copyMyElements.gsp b/grails-app/views/license/copyMyElements.gsp index 951529c627..a4d46ef3ea 100644 --- a/grails-app/views/license/copyMyElements.gsp +++ b/grails-app/views/license/copyMyElements.gsp @@ -1,4 +1,4 @@ -<%@ page import="de.laser.ui.Icon; de.laser.Person; de.laser.RefdataValue; de.laser.LicenseController; de.laser.CopyElementsService" %> +<%@page import="de.laser.ui.Icon; de.laser.addressbook.Person; de.laser.RefdataValue; de.laser.LicenseController; de.laser.CopyElementsService" %> diff --git a/grails-app/views/myInstitution/subscriptionsManagement.gsp b/grails-app/views/myInstitution/subscriptionsManagement.gsp index 97d88dceb0..7bef1c8887 100644 --- a/grails-app/views/myInstitution/subscriptionsManagement.gsp +++ b/grails-app/views/myInstitution/subscriptionsManagement.gsp @@ -12,6 +12,10 @@ + + + + diff --git a/grails-app/views/package/_planned_expired_deleted.gsp b/grails-app/views/package/_planned_expired_deleted.gsp index ad13bb8e70..0948f6af1f 100644 --- a/grails-app/views/package/_planned_expired_deleted.gsp +++ b/grails-app/views/package/_planned_expired_deleted.gsp @@ -59,7 +59,7 @@ - KBART Export @@ -120,7 +120,7 @@ e.preventDefault(); $('#globalLoadingIndicator').show(); $.ajax({ - url: "", + url: "", type: 'POST', contentType: false }).done(function(response){ diff --git a/grails-app/views/subscription/index.gsp b/grails-app/views/subscription/index.gsp index 5ed7475eb9..921995e6f3 100644 --- a/grails-app/views/subscription/index.gsp +++ b/grails-app/views/subscription/index.gsp @@ -7,7 +7,8 @@ - +<%-- deactivated because there are no differences between basic and advanced modes +--%> diff --git a/grails-app/views/subscription/show.gsp b/grails-app/views/subscription/show.gsp index 921cb1ed6c..d53951e942 100644 --- a/grails-app/views/subscription/show.gsp +++ b/grails-app/views/subscription/show.gsp @@ -436,7 +436,7 @@
${message(code: 'subscription.holdingSelection.label')}
+ config="${RDConstants.SUBSCRIPTION_HOLDING}" overwriteEditable="${editable && !AuditConfig.getConfig(subscription.instanceOf, 'holdingSelection')}"/>
- + + + +
+ + %{-- + noSelection='["": "${message(code: 'subscriptionsManagement.noSelection.package')}"]'/>--}%
- + @@ -295,6 +301,31 @@ }); } + JSPC.app.ajaxDropdown = function(selector, url, valuesString) { + let values = []; + if(valuesString.includes(',')) { + values = valuesString.split(','); + } + else if(valuesString.length > 0) { + values.push(valuesString); + } + selector.dropdown({ + apiSettings: { + url: url, + cache: false + }, + clearable: true, + minCharacters: 0 + }); + if(values.length > 0) { + selector.dropdown('queryRemote', '', () => { + selector.dropdown('set selected', values); + }); + } + } + + JSPC.app.ajaxDropdown($("#selectedPackages"), "${createLink([controller:"ajaxJson", action:"lookupPackages"])}?query={query}", ''); + $('.packagesForm').form({ on: 'blur', inline: true, diff --git a/src/main/groovy/de/laser/api/v0/ApiCollectionReader.groovy b/src/main/groovy/de/laser/api/v0/ApiCollectionReader.groovy index 9cd93ad544..361e86773b 100644 --- a/src/main/groovy/de/laser/api/v0/ApiCollectionReader.groovy +++ b/src/main/groovy/de/laser/api/v0/ApiCollectionReader.groovy @@ -33,6 +33,7 @@ import groovy.json.JsonSlurper import groovy.sql.GroovyRowResult import groovy.sql.Sql import groovy.util.logging.Slf4j +import groovyx.gpars.GParsPool /** * This class delivers given lists as maps of stubs or full objects @@ -352,7 +353,11 @@ class ApiCollectionReader { [sub: subPkg.subscription, pkg: subPkg.pkg, statusTipp: RDStore.TIPP_STATUS_REMOVED, statusIe: RDStore.TIPP_STATUS_REMOVED] ) */ - Map subParams = [subId: subPkg.subscription.id], pkgParams = [pkgId: subPkg.pkg.id], ieParams = [sub: subPkg.subscription.id, pkg: subPkg.pkg.id] + Subscription targetSub + if(subPkg.subscription.instanceOf && subPkg.subscription.holdingSelection == RDStore.SUBSCRIPTION_HOLDING_ENTIRE) + targetSub = subPkg.subscription.instanceOf + else targetSub = subPkg.subscription + Map subParams = [subId: targetSub.id], pkgParams = [pkgId: subPkg.pkg.id], ieParams = [sub: targetSub.id, pkg: subPkg.pkg.id] int limit = 50000, ieCount = sql.rows("select count(*) from issue_entitlement join title_instance_package_platform on ie_tipp_fk = tipp_id where ie_subscription_fk = :sub and tipp_pkg_fk = :pkg", ieParams)[0]["count"] List ieRows = [] for(int i = 0; i < ieCount; i += limit) { @@ -392,48 +397,51 @@ class ApiCollectionReader { Map pkgData = packageOfSubscription.get(0) pkgData.ids = packageIDs pkgData.altnames = packageAltNames - ieRows.eachWithIndex{ GroovyRowResult row, int i -> - //println "now processing row ${i}" - //result << ApiIssueEntitlement.getIssueEntitlementMap(ie, ignoreRelation, context) // de.laser.IssueEntitlement - Map ie = [globalUID: row['ie_guid']] - //ie.name = row['ie_name'] - ie.accessStartDate = row['ie_access_start_date'] ? ApiToolkit.formatInternalDate(row['ie_access_start_date']) : null - ie.accessEndDate = row['ie_access_end_date'] ? ApiToolkit.formatInternalDate(row['ie_access_end_date']) : null - ie.lastUpdated = row['ie_last_updated'] ? ApiToolkit.formatInternalDate(row['ie_last_updated']) : null - //RefdataValues - both removed as of API version 2.0 - //ie.medium = row['ie_medium'] - ie.status = row['ie_status'] - ie.perpetualAccessBySub = ApiStubReader.requestSubscriptionStub(Subscription.get(row['ie_perpetual_access_by_sub_fk']), context, false) - ie.coverages = coverageMap.containsKey(row['tipp_id']) ? coverageMap.get(row['tipp_id']) : [] - ie.priceItems = priceItemMap.containsKey(row['tipp_id']) ? priceItemMap.get(row['tipp_id']) : [] - //References - row.ids = identifierMap.containsKey(row['tipp_id']) ? identifierMap.get(row['tipp_id']) : [] - row.ddcs = ddcMap.containsKey(row['tipp_id']) ? ddcMap.get(row['tipp_id']) : [] - row.languages = languageMap.containsKey(row['tipp_id']) ? languageMap.get(row['tipp_id']) : [] - row.altnames = altNameMap.containsKey(row['tipp_id']) ? altNameMap.get(row['tipp_id']) : [] - row.publishers = [] //publisherMap.containsKey(row['tipp_id']) ? publisherMap.get(row['tipp_id']) : [] - if(ignoreRelation != ApiReader.IGNORE_ALL) { - //println "processing references" - if(ignoreRelation == ApiReader.IGNORE_SUBSCRIPTION_AND_PACKAGE) { - //row.platform = platformMap.get(row['tipp_plat_fk'])[0] - row.pkg = pkgData - ie.tipp = ApiMapReader.getTippMapWithSQL(row, ApiReader.IGNORE_ALL, context) // de.laser.wekb.TitleInstancePackagePlatform - } - else { - if(ignoreRelation != ApiReader.IGNORE_TIPP) { - //row.platform = platformMap.get(row['tipp_plat_fk'])[0] - row.pkg = pkgData - ie.tipp = ApiMapReader.getTippMapWithSQL(row, ApiReader.IGNORE_SUBSCRIPTION, context) // de.laser.wekb.TitleInstancePackagePlatform - } - if(ignoreRelation != ApiReader.IGNORE_SUBSCRIPTION) { - ie.subscription = ApiStubReader.requestSubscriptionStub(subPkg.subscription, context) // de.laser.wekb.TitleInstancePackagePlatform + GParsPool.withPool(8) { + ieRows.eachWithIndexParallel{ GroovyRowResult row, int i -> + Subscription.withTransaction { + //println "now processing row ${i}" + //result << ApiIssueEntitlement.getIssueEntitlementMap(ie, ignoreRelation, context) // de.laser.IssueEntitlement + Map ie = [globalUID: row['ie_guid']] + //ie.name = row['ie_name'] + ie.accessStartDate = row['ie_access_start_date'] ? ApiToolkit.formatInternalDate(row['ie_access_start_date']) : null + ie.accessEndDate = row['ie_access_end_date'] ? ApiToolkit.formatInternalDate(row['ie_access_end_date']) : null + ie.lastUpdated = row['ie_last_updated'] ? ApiToolkit.formatInternalDate(row['ie_last_updated']) : null + //RefdataValues - both removed as of API version 2.0 + //ie.medium = row['ie_medium'] + ie.status = row['ie_status'] + ie.perpetualAccessBySub = ApiStubReader.requestSubscriptionStub(Subscription.get(row['ie_perpetual_access_by_sub_fk']), context, false) + ie.coverages = coverageMap.containsKey(row['tipp_id']) ? coverageMap.get(row['tipp_id']) : [] + ie.priceItems = priceItemMap.containsKey(row['tipp_id']) ? priceItemMap.get(row['tipp_id']) : [] + //References + row.ids = identifierMap.containsKey(row['tipp_id']) ? identifierMap.get(row['tipp_id']) : [] + row.ddcs = ddcMap.containsKey(row['tipp_id']) ? ddcMap.get(row['tipp_id']) : [] + row.languages = languageMap.containsKey(row['tipp_id']) ? languageMap.get(row['tipp_id']) : [] + row.altnames = altNameMap.containsKey(row['tipp_id']) ? altNameMap.get(row['tipp_id']) : [] + row.publishers = [] //publisherMap.containsKey(row['tipp_id']) ? publisherMap.get(row['tipp_id']) : [] + if(ignoreRelation != ApiReader.IGNORE_ALL) { + //println "processing references" + if(ignoreRelation == ApiReader.IGNORE_SUBSCRIPTION_AND_PACKAGE) { + //row.platform = platformMap.get(row['tipp_plat_fk'])[0] + row.pkg = pkgData + ie.tipp = ApiMapReader.getTippMapWithSQL(row, ApiReader.IGNORE_ALL, context) // de.laser.wekb.TitleInstancePackagePlatform + } + else { + if(ignoreRelation != ApiReader.IGNORE_TIPP) { + //row.platform = platformMap.get(row['tipp_plat_fk'])[0] + row.pkg = pkgData + ie.tipp = ApiMapReader.getTippMapWithSQL(row, ApiReader.IGNORE_SUBSCRIPTION, context) // de.laser.wekb.TitleInstancePackagePlatform + } + if(ignoreRelation != ApiReader.IGNORE_SUBSCRIPTION) { + ie.subscription = ApiStubReader.requestSubscriptionStub(subPkg.subscription, context) // de.laser.wekb.TitleInstancePackagePlatform + } + } } + //println "processing finished" + result << ApiToolkit.cleanUp(ie, true, true) } } - //println "processing finished" - result << ApiToolkit.cleanUp(ie, true, true) } - return ApiToolkit.cleanUp(result, true, true) }