Pat 162007723
Pat 162007723
#
# Issue: SFWK-12709
# Module:
# Version: Content Server 16.2.0 64-bit for Windows
# Update: 24.2.0 (2024-03),24.1.0 (2023-12)
#
# Description:
# 24.1 - 24.2 hot-fix: Recommender fails to populate Content Server UI due to NULL
value in database table
#
# #
m {'kernel','lliapi','recommender'}
o {'pat::GatherProcessing',&&recommender::GatherProcessing}
N GatherProcessing
f CursorThroughData
s
/*
* Recommender Agent processing script.
* This script processes the data stored in the RECD_OperationTracking table
into the Recd_OperationSummary and Recd_Hot tables.
*
* @param {Object} prgCtx A program context object.
*
* @return {Void}
*
*/
function Void CursorThroughData(
Object prgCtx )
Record rec
Integer recordCounter
Dynamic interestData
Dynamic endTick
String stmt
List recordData
if ( ok )
repeat
ok = ._Setup( prgCtx )
breakIf ( ! ok )
if ( IsNotError( interestData ) )
ok = ._ValidateDataIDs( prgCtx,
RecArray.ColumnToList( interestData, "DataID" ) )
ok = ._ProcessInterest( rec )
breakIf ( ! ok )
if ( recordCounter == .fBLOCKSIZE )
breakIf ( ! ok )
ok = FALSE
EchoError( Str.Format( '%1.CursorThroughData: Failed to get
all of the interest rows: %2',
OS.Name( this ), Str.String( interestData
) ) )
break
end
ok = ._UpdateOperationSummaryTables()
breakIf ( ! ok )
ok = ._CleanUpSystemStatsTables()
breakIf ( ! ok )
if ( ok )
._FilterSummaryTable()
._Cleanup()
if ( ok )
end
sEND
f _BuildAccessKey
s
return Undefined
end
sEND
g _CheckForNulls
s
/*
* Checks that all the database values are not null
*
* @param {Record} rec The record from the database
*
* @return {Boolean} FALSE if one or more values is NULL, TRUE
otherwise
*
*/
function Boolean _CheckForNulls(
Record rec )
String column
Boolean ok = TRUE
List fieldNames = RecArray.FieldNames( rec )
ok = FALSE
break
end
end
return ok
end
sEND
f _CleanUpSystemStatsTables
s
/*
* Cleanup the Recd_SystemStatsSummary table
*
* @return {Boolean} TRUE if OK, FALSE otherwise
*
*/
function Boolean _CleanUpSystemStatsTables()
Dynamic dbResult
String stmt
List bindValues
Boolean ok = TRUE
CAPIConnect conn = .fPrgCtx.fDbConnect.fConnection
Object dapiCtx = .fPrgCtx.DSession()
if ( dapiCtx.StartTrans() )
ok = FALSE
EchoError( Str.Format( '%1._CleanUpSystemStatsTables: Could not
update Recd_SystemStatsSummary table: %2', OS.Name( this ),
Str.String( dbResult ) ) )
else
// Need to update the refcount in the RDB and this can be done in
a single update statement
stmt = "UPDATE Recd_SystemStatsSummary SET RefCount = COALESCE( (
SELECT SUM( RefCount ) AS refCount FROM Recd_OperationSummary ), 0 )"
if ( IsError( dbResult ) )
ok = FALSE
EchoError( Str.Format( "%1._CleanUpSystemStatsTables: Could
not update Recd_SystemStatsSummary table's RefCount: %2", OS.Name( this ),
Str.String( dbResult ) ) )
end
end
if ( ! dapiCtx.EndTrans( ok ) )
return ok
end
sEND
f _Cleanup
s
/*
* Reset all the features in this object to their defaults
*
* @return {Void}
*
*/
function Void _Cleanup()
.fPrgCtx = Undefined
.fSystemStats = Undefined
.fRollUpDataIDs = {}
.fUpdateAssoc = Undefined
.fInsertAssoc = Undefined
.fValidSubTypeMapping = Undefined
.fInterestRowsToCommitRecArray = Undefined
.fNumRowsProcessed = 0
.fStartTick = Undefined
end
sEND
f _ClearCounts
s
end
sEND
f _FillInMissingSubTypes
s
return Undefined
end
sEND
f _FilterSummaryTable
s
/*
* Filters out any unnecessary data from the Summary table.
*
* @return {Boolean} TRUE if OK, FALSE otherwise
*
*/
function Boolean _FilterSummaryTable()
Date earliestAllowableDate
Dynamic dbResult
String stmt
String tableName
String template
Boolean ok = TRUE
Object dbConnect = .fPrgCtx.fDbConnect
Object recommenderUtils =
$RECOMMENDER.RecommenderUtils
String excludedNodesSQL =
recommenderUtils.GetExcludedNodesSQL( .fPrgCtx, "DTreeOwners.SubType" )
Assoc config =
recommenderUtils.GetRecommenderAdminConfig( .fPrgCtx )
CAPIConnect conn = .fPrgCtx.fDbConnect.fConnection
List tablesList = { "Recd_OpinionTracking",
"Recd_OpinionSummary", "Recd_OperationSummary" }
Integer dateDays = Date.DateInteger( Date.Now() )
Object dapiCtx = .fPrgCtx.DSession()
String LOGPREFIX =
Str.Format( '%1._FilterSummaryTable:', OS.Name( this ) )
if ( dapiCtx.StartTrans() )
if ( IsError( dbResult ) )
ok = FALSE
EchoError( Str.Format( '%1 Delete from
Recd_OperationSummary for UserID failed. Error: %2', LOGPREFIX,
Str.String( dbResult ) ) )
end
end
if ( ok )
if ( .fDateInDaysCleanup != dateDays )
ok = FALSE
EchoError( Str.Format( '%1 Delete from
Recd_OperationSummary for RecentAccess failed. Error: %2', LOGPREFIX,
Str.String( dbResult ) ) )
end
end
end
end
if ( IsNotError( dbResult ) )
if ( ok )
//Filter out all the temporary objects from the operation summary
table
if ( IsDefined( excludedNodesSQL ) )
if ( tableName == "Recd_OpinionTracking" )
if ( IsError( dbResult ) )
ok = FALSE
EchoError( Str.Format( '%1 Delete failed on
table %2. Error: %3', LOGPREFIX, tableName, Str.String( dbResult ) ) )
break
end
end
end
end
if ( ok )
if ( tableName == "Recd_OpinionTracking" )
if ( IsError( dbResult ) )
ok = FALSE
EchoError( Str.Format( '%1 Delete failed on table %2.
Error: %3', LOGPREFIX, tableName, Str.String( dbResult ) ) )
break
end
end
end
if ( ok )
// Go and update the date of this run for the next batch
// using the RDB date
.fSystemDate = CAPI.Now( conn )
if ( IsNotError( .fSystemDate ) )
if ( IsError( dbResult ) )
ok = FALSE
EchoError( Str.Format( "%1 Error saving SystemDate to
database. Error: %2", LOGPREFIX, Str.String( dbResult ) ) )
end
else
ok = FALSE
EchoError( Str.Format( "%1 Error retrieving the SystemDate
from the database. Error: %2", LOGPREFIX, Str.String( .fSystemDate ) ) )
.fSystemDate = Undefined
end
end
if ( ok )
List ELSubTypesList
String ELSubTypesString
Integer typeCount
Integer typeLength
if ( IsError( dbResult ) )
ok = FALSE
EchoError( Str.Format( "%1 Error executing
delete for Enterprise Library. Error: %2", LOGPREFIX, Str.String( dbResult ) ) )
break
end
end
end
end
if ( ! dapiCtx.EndTrans( ok ) )
return ok
end
sEND
g _FindDataFromPathInfo
s
/*
* Retrieve the DataID from a given path
*
* @param {String} pathInfo The path information
*
* @return {Integer} dataID The DataID retrieved (Undefined if
not found)
*
*/
function Integer _FindDataFromPathInfo( String pathInfo )
Dynamic parentID
Dynamic dbResult
Integer i
String checkFetch
String nodeName
if ( IsDefined( checkID ) )
dataID = checkID
keepGoing = FALSE
break
end
end
end
if ( keepGoing )
break
end
end
nodeName = elements[ i + 1 ]
parentID = elements[ i ]
dataID = Undefined
i += 1
nodeName = elements[ i + 1 ]
parentID = dataID
end
end
return dataID
end
sEND
g _FindDataFromPathInfoForHH
s
/*
* Retrieve the node information from a given path
*
* @param {String} pathInfo The path information
*
* @return {Integer} dataID The DataID retrieved (Undefined if
not found)
*
*/
function Integer _FindDataFromPathInfoForHH( String pathInfo )
Integer i
String nodeName
String theDataID
break
end
end
nodeName = elements[ i ]
if ( IsDefined( nodeName ) )
if ( IsDefined( theDataID ) )
Assoc checkVal
Dynamic totalTick
Dynamic dbResult
Real rowspersec
Record rec
Boolean ok = TRUE
RecArray recs = RecArray.Create( { 'DataID', 'SectionID',
'SubType' } )
CAPIConnect conn = prgCtx.fDbConnect.fConnection
Dynamic startTick = Date.Tick()
if ( checkVal.ok == TRUE )
if ( IsError( dbResult ) )
ok = FALSE
ok = FALSE
EchoError( Str.Format( "%1._GetDataAndInsertIntoHotTable: Error
running RecommenderUtils function [%2]. Error: %3", OS.Name( this ), functionCall,
checkVal.errMsg ) )
end
if ( ok )
return ok
end
sEND
f _GetRecArrayInsert
s
function RecArray _GetRecArrayInsert()
return Undefined
end
sEND
f _GetRecArrayUpdate
s
return Undefined
end
sEND
f _GetReference
s
return Undefined
end
return Undefined
end
sEND
f _GetRollupUserRecArrayInsert
s
return Undefined
end
sEND
f _PopulateHotTable
s
/*
* Populates the Recd_Hot table.
*
* @return {Boolean} TRUE if OK, FALSE otherwise
*
*/
function Boolean _PopulateHotTable()
Assoc adminConfig
Dynamic dbResult
Integer i
Integer subtype
List enabledPopularSections
List subtypeList
String section
String functionCall
Boolean ok = TRUE
Object recommenderUtils = $RECOMMENDER.RecommenderUtils
Object prgCtx = .fPrgCtx
Object dapiCtx = .fPrgCtx.DSession()
if ( dapiCtx.StartTrans() )
enabledPopularSections = adminConfig.aboutMeSections
else
ok = FALSE
EchoError( Str.Format( "%1._PopulateHotTable: Could not retrieve
Recommender settings.", OS.Name( this ) ) )
end
if ( ok )
if ( IsError( dbResult ) )
ok = FALSE
EchoError( Str.Format( "%1._PopulateHotTable: Could not
empty Recd_Hot table. Error: %2", OS.Name( this ), Str.String( dbResult ) ) )
end
end
if ( ok )
case "whatsnew"
i = 1
end
case "mostactive"
i = 2
end
case "toppicks"
i = 3
end
end
if ( IsUndefined( subtypeList ) ||
( Length( subtypeList ) == 0 ) )
ok = ._GetDataAndInsertIntoHotTable( prgCtx,
subtypeList,
adminConfig.
( section ).samplesize, i, functionCall,
recommenderUtils.GetSectionDisplayname( section ) )
breakIf ( ! ok )
else
ok
= ._GetDataAndInsertIntoHotTable( prgCtx, { subtype },
adminConfig.
( section ).samplesize, i, functionCall,
recommenderUtils.GetSectionDisplayname( section ) )
breakIf ( ! ok )
end
end
end
end
end
if ( ! dapiCtx.EndTrans( ok ) )
return Undefined
end
sEND
f _ProcessInterest
s
/*
* For the given record, process its tracking data.
*
* @param {Record} rec The record of data to process.
*
* @return {Boolean} TRUE if OK, FALSE otherwise
*
*/
function Boolean _ProcessInterest( \
Record rec )
Dynamic dbResult
Assoc refAssoc
Integer subType
List bindArgs
String keyString
Boolean ok = TRUE
CAPIConnect conn = .fPrgCtx.fDbConnect.fConnection
List keyNames = Assoc.Keys( .fValidSubTypeMapping )
Integer dateInt = Date.DateInteger( rec.OperationDate )
Integer timeInt = Date.TimeInteger( rec.OperationDate )
// Only skip row if it isn't in DTree (has been deleted since it was added to
Recd_OperationTracking)
if ( rec.DataID in keyNames )
if ( IsNotError( dbResult ) )
.fUpdateAssoc.( keyString ) =
RecArray.CreateRecord( .fSummaryTableListUpdate, { rec.OperationDate,
dbResult[ 1 ].RefCount,
rec.UserID,
rec.DataID,
subType,
rec.Action } )
else
.fInsertAssoc.( keyString ) =
RecArray.CreateRecord( .fSummaryTableList, { rec.OperationDate,
rec.OperationDate,
$RECOMMENDER.RecommenderUtils.ComputeNewMean( 0, dateInt, 1 ),
rec.UserID,
rec.DataID,
rec.ObjType,
rec.Action,
1,
subType } )
end
ok = FALSE
EchoError( Str.Format( "%1._ProcessInterest: Could not
retrieve data from Recd_OperationSummary table. Error: %2", OS.Name( this ),
Str.String( dbResult ) ) )
end
else
return ok
end
return Undefined
end
return Undefined
end
sEND
g _ProcessUnknownDataIDs
s
/**
*
* Tries to retrieve any unknown DataID's (-1) that were included in the tracking
table.
* We do that by seeing if there is a value in the PathInfo column. If so, we can
process that to extract the DataID.
* Updates the original record in the RecArray.
*
*/
function Void _ProcessUnknownDataIDs(
RecArray recs )
Integer dataID
Integer i
dataID
= ._FindDataFromPathInfo( Str.Trim( recs[ i ].PathInfo ) )
else
dataID
= ._FindDataFromPathInfoForHH( Str.Trim( recs[ i ].PathInfo ) )
end
if ( IsDefined( dataID ) )
Dynamic dbResult
integer i
Boolean ok = TRUE
CAPIConnect conn = prgCtx.fDbConnect.fConnection
.fPrgCtx = prgCtx
if ( ! localSetup )
.fSystemStats = Assoc.CreateAssoc()
if ( IsNotError( dbResult ) )
ok = FALSE
EchoError( Str.Format( "%1._Setup: Error retrieving data from the
Recd_SystemStatsSummary table. Error: %2", OS.Name( this ),
Str.String( dbResult ) ) )
end
if ( IsNotError( dbResult ) )
if ( IsUndefined( .fSystemStats.RefCount ) )
.fSystemStats.RefCount = 1
end
end
end
if ( ok )
.fRollUpDataIDs = {}
.fUpdateAssoc = Assoc.CreateAssoc()
.fInsertAssoc = Assoc.CreateAssoc()
.fValidSubTypeMapping = Assoc.CreateAssoc()
if ( Length( .fColNames ) == 0 )
for i = 1 to .fBLOCKSIZE
if ( Length( .fBindVars ) == 0 )
// The min amount of time the gather should run for. As soon as this
value has been exceeded then stop the gather
.fTimeOfExecutionInSec =
$KERNEL.SystemPreferences.GetPrefInteger( 'Recommender', 'MinExecutionTimeInSecs',
120 )
if ( .fTimeOfExecutionInSec < 0 )
.fTimeOfExecutionInSec = 120
end
end
return ok
end
sEND
f _UpdateOperationSummaryTables
s
/*
* Insert the data processed from the Recd_OperationTracking table into the
* Recd_OperationSummary table and Recd_Hot table.
* Delete the processed rows from the Recd_OperationTracking table.
*
* @return {Boolean} TRUE if OK, FALSE otherwise
*
*/
function Boolean _UpdateOperationSummaryTables()
Integer totalTick
Dynamic dbResult
Dynamic rowspersec
Integer dataID
Integer userID
Integer user
Integer i
Integer assocLength
List item
List binds
List rollupTwoThreeUsers
List assocRecords
RecArray recs
Record selectRec
String action
String stmt
String stmtDelete
Boolean ok = TRUE
Object dbConnect = .fPrgCtx.fDbConnect
CAPIConnect conn = .fPrgCtx.fDbConnect.fConnection
Object dapiCtx = .fPrgCtx.DSession()
Integer startTick = Date.Tick()
// actionViewableWhereClause will return a "Action IN ( x, x )" string with
all the actions that are tracked by Recommender
String actionViewableWhereClause =
$RECOMMENDER.RecommenderUtils.GetViewableList( "" )
List viewableActions =
$RECOMMENDER.RecommenderUtils.GetViewableActions()
String rollupUserSQLSelect = "SELECT MIN( FirstAccess ) AS
FirstAccess, MAX( RecentAccess ) AS RecentAccess, SUM( MeanDate * RefCount ) / SUM(
RefCount ) AS MeanDate, SUM( MeanSqDate * RefCount ) / SUM( RefCount ) AS
MeanSqDate, " +
"SUM( MeanTime *
RefCount ) / SUM( RefCount ) AS MeanTime, SUM( MeanSqTime * RefCount ) /
SUM( RefCount ) AS MeanSqTime, SUM( RefCount ) AS RefCount, MAX( ObjType ) AS
ObjType, MAX( SubType ) AS SubType " +
"FROM
Recd_OperationSummary WHERE DataID = :A1 AND UserID != :A2 AND Action = :A3 GROUP
BY SubType"
String rollupTwoThreeSQLSelect = Str.Substitute( "SELECT
MIN( FirstAccess ) AS FirstAccess, MAX( RecentAccess ) AS RecentAccess,
SUM( MeanDate * RefCount ) / SUM( RefCount ) AS MeanDate, SUM( MeanSqDate *
RefCount ) / SUM( RefCount ) AS MeanSqDate, " +
"SUM( MeanTime *
RefCount ) / SUM( RefCount ) AS MeanTime, SUM( MeanSqTime * RefCount ) /
SUM( RefCount ) AS MeanSqTime, SUM( RefCount ) AS RefCount, MAX( ObjType ) AS
ObjType, MAX( SubType ) AS SubType " +
"FROM
Recd_OperationSummary WHERE %Action% AND DataID = :A1 AND UserID %1= :A2 GROUP BY
SubType", { { "Action", actionViewableWhereClause } } )
String rollupExistingSelect = "SELECT 1 FROM
Recd_OperationSummary WHERE DataID = :A1 AND UserID = :A2 AND Action = :A3"
String rollupUpdateStmt = "UPDATE Recd_OperationSummary SET
RecentAccess = :A1, MeanDate = :A2, MeanSqDate = :A3, MeanTime = :A4, MeanSqTime
= :A5, RefCount = :A6 " +
"WHERE DataID
= :A7 AND UserID = :A8 AND Action = :A9"
String rollupInsertStmt = "INSERT INTO Recd_OperationSummary
( FirstAccess, RecentAccess, MeanDate, MeanSqDate, MeanTime, MeanSqTime, UserID,
DataID, ObjType, Action, RefCount, SubType ) " +
"VALUES( :A1, :A2,
:A3, :A4, :A5, :A6, :A7, :A8, :A9, :A10, :A11, :A12 )"
// The use of GROUP BY ensures that if there is no data and a math operation
is performed, that undefined values are not returned (instead no rows are
returned).
// We also want to ensure that there is only 1 row returned (and not multiple
when using GROUP BY) so we need to ensure the GROUP BY column only has one value
(hence using the maximum SubType number. Using minimum might return -1 which is
unknown values).
if ( dapiCtx.StartTrans() )
// This first part insert new rows into the summary table when
// there is a new row based on UserID/DataID/Action
if ( assocLength > 0 )
for i = 1 To assocLength
RecArray.SetRecord( recs, assocRecords[ i ], i )
end
if ( IsError( dbResult ) )
ok = FALSE
EchoError( Str.Format( "%1._UpdateOperationSummaryTables:
Error inserting rows into Recd_OperationSummary table. Error: %2", OS.Name( this ),
Str.String( dbResult ) ) )
end
end
// This part updates existing rows in the summary table, again based
// on a row anchored by UserID/DataID/SubType/Action
if ( ok )
if ( assocLength > 0 )
for i = 1 To assocLength
if ( IsError( dbResult ) )
ok = FALSE
EchoError( Str.Format( "%1._UpdateOperationSummaryTables: Error updating rows
in Recd_OperationSummary table. Error: %2", OS.Name( this ), Str.String( dbResult )
) )
end
end
end
// This section deals with the rollups of the summary table entries
that have been added or updated.
dataID = item[ 1 ]
action = item[ 2 ]
userID = item[ 3 ]
// for each of the dataIDs that have been operated on, see
if there is
// an existing rollup row in the summary table.
if ( action in viewableActions )
if ( ._CheckForNulls( selectRec ) )
// Check to see if the row already exists
dbResult = CAPI.Exec( conn,
rollupExistingSelect, binds )
if ( IsNotError( dbResult ) )
selectRec.MeanDate,
selectRec.MeanSqDate,
selectRec.MeanTime,
selectRec.MeanSqTime,
selectRec.RefCount,
@binds } )
if ( IsError( dbResult ) )
ok = FALSE
break
end
else
selectRec.RecentAccess,
selectRec.MeanDate,
selectRec.MeanSqDate,
selectRec.MeanTime,
selectRec.MeanSqTime,
-1,
dataID,
selectRec.ObjType,
action,
selectRec.RefCount,
selectRec.SubType } )
if ( IsNotError( dbResult ) )
if ( dbResult != 1 )
// If the insert
returns zero rows then skip the next update
ok = FALSE
break
end
end
else
ok = FALSE
break
end
end
if ( item in .fRollUpDataIDs )
selectRec = dbResult[ 1 ]
if ( ._CheckForNulls( selectRec ) )
if ( IsNotError( dbResult ) )
dbResult =
CAPI.Exec( conn, rollupUpdateStmt, { selectRec.RecentAccess,
selectRec.MeanDate,
selectRec.MeanSqDate,
selectRec.MeanTime,
selectRec.MeanSqTime,
selectRec.RefCount,
@binds,
'x' } )
if ( IsError( dbResult )
)
ok = FALSE
break
end
else
selectRec.RecentAccess,
selectRec.MeanDate,
selectRec.MeanSqDate,
selectRec.MeanTime,
selectRec.MeanSqTime,
user,
dataID,
selectRec.ObjType,
'x',
selectRec.RefCount,
selectRec.SubType } )
// We will fail silently
because there is no recourse for not succeeding.
if ( IsError( dbResult )
)
ok = FALSE
break
end
end
else
ok = FALSE
break
end
end
breakIf ( !ok )
end
end
end
end
if ( ok )
if ( IsError( dbResult ) )
ok = FALSE
if ( ! dapiCtx.EndTrans( ok ) )
if ( ok )
if ( totalTick > 0 )
if ( .fNumRowsProcessed > 0 )
return ok
end
sEND
f _UpdateSystemStats
s
/*
* Updates the SystemStats table with the latest dates/times.
*
* @param {Record} rec The record that contains the
OperationDate
*
* @return {Void}
*
*/
function void _UpdateSystemStats( Record rec )
.fSystemStats.meanDate =
$RECOMMENDER.RecommenderUtils.ComputeNewMean( .fSystemStats.MeanDate,
dateInt, .fSystemStats.RefCount )
.fSystemStats.meanSqDate =
$RECOMMENDER.RecommenderUtils.ComputeNewMean( .fSystemStats.MeanSqDate, dateInt *
dateInt, .fSystemStats.RefCount )
.fSystemStats.meanTime =
$RECOMMENDER.RecommenderUtils.ComputeNewMean( .fSystemStats.MeanTime,
timeInt, .fSystemStats.RefCount )
.fSystemStats.meanSqTime =
$RECOMMENDER.RecommenderUtils.ComputeNewMean( .fSystemStats.MeanSqTime, timeInt *
timeInt, .fSystemStats.RefCount )
end
sEND
f _ValidateDataIDs
s
/*
* For the list of IDs passed in, check to see if they exist in DTree.
* If they do not, they will be skipped for processing in _ProcessInterest().
* Also, do not process any IDs if their catalog value is set to hidden.
*
* @param {Object} prgCtx A program context object.
* @param {List} dataIDs A list of DataIDs to check.
*
* @return {Boolean} TRUE if OK, FALSE otherwise
*
*/
function Boolean _ValidateDataIDs(
Object prgCtx,
List dataIDs )
Assoc checkVal
Boolean ok = TRUE
Object dbConnect = prgCtx.fDBConnect
String stmt = "SELECT DataID, SubType FROM DTree WHERE %1 AND
Catalog != 2"
List IDsToCheck = $KERNEL.DataTools.RemoveDuplicates( dataIDs )
if ( checkVal.OK == TRUE )
if ( checkVal.ok == TRUE )
.fValidSubTypeMapping = checkVal.theAssoc
else
ok = FALSE
EchoError( Str.Format( "%1._ValidateDataIDs: Duplicate DataIDs
found in response when there shouldn't be.", OS.Name( this ) ) )
end
else
ok = FALSE
EchoError( Str.Format( "%1._ValidateDataIDs: Error selecting rows from
DTree table. Error: %2", OS.Name( this ), checkVal.errMsg ) )
end
return ok
end
sEND
f fBindVars
v ''
f fPrgCtx
v ?
f fRollUpDataIDs
v {}
g fValidSubTypeMapping
v ?
o {'pat::RecArrayPkg',&&lliapi::RecArrayPkg}
N RecArrayPkg
g AssocFromColumns
s
/**
* Converts 2 columns in a RecArray into a single Assoc
*
* @param theArray {RecArray} The RecArray
* @param keyNameColumn {Dynamic} The Assoc's key value column. Can either
be the name of the column
* or the index. Each value
in the column must be unique.
* @param valueNameColumn {Dynamic} The Assoc's key's value column. Can
either be the name of the column
* or the index.
* @param skipChecking {Boolean} (Optional) By default will check for
duplicate key names. Set to TRUE
* to allow key value
overwriting if duplicate keys are found.
*
* @return {Assoc}
* @returnFeature {Boolean} ok TRUE if OK, FALSE if
duplicates found and skipping was not skippped.
* @returnFeature {Assoc} theAssoc The new Assoc
*/
function Assoc AssocFromColumns(
RecArray theArray,
Dynamic keyNameColumn,
Dynamic valueNameColumn,
Boolean skipChecking = FALSE )
Dynamic keyName
Dynamic value
Integer i
Assoc retVal
Assoc newAssoc
Record row
Boolean ok = TRUE
Boolean isKeyNameString = ( Type( keyNameColumn ) == StringType )
Boolean isValueNameString = ( Type( valueNameColumn ) == StringType
)
row = theArray[ i ]
keyName = isKeyNameString ? row.( keyNameColumn ) :
row[ keyNameColumn ]
value = isValueNameString ? row.( valueNameColumn ) :
row[ valueNameColumn ]
ok = FALSE
EchoError( Str.Format( "%1.AssocFromColumns: Duplicate key name
found, cannot convert RecArray. Duplicate key name: %2",
OS.Name( this ),
keyName ) )
break
else
return retVal
end
sEND
o {'pat::DataTools',&&kernel::UtilityClasses::DataTools}
N DataTools
g RemoveDuplicates
s
/**
* Remove any duplicates from a list or RecArray.
*
* @param {Dynamic} original A list or RecArray to remove the duplicates from
* @param {Dynamic} column (Optional) If a RecArray, specify the column
that you would like to remove the duplicates from.
* @param {Boolean} keepOrder (Optional) If FALSE, we use a faster method of
removing duplicates from a list by converting to a set,
* then converting back into a list.
This will have the side effect of sorting the list. If ordering matters,
* then set this to TRUE to use the
slower method of iteration over each item.
*
* @return {Dynamic} A list or RecArray (same as what was passed in)
*/
Dynamic item
HashMap uniqueItems
Dynamic retVal
Dynamic columnValue
Set uniqueSet
if ( ! keepOrder )
retVal = {}
for item in original
retVal += { item }
uniqueItems.( item ) = TRUE
end
end
end
RecArray.AddRecord( retVal,
RecArray.GetRecord( item ) )
uniqueItems.( columnValue ) = TRUE
end
end
end
else
return retVal
end
sEND
o {'pat::RecommenderUtils',&&recommender::RecommenderUtils}
N RecommenderUtils
f GetViewableList
s
/**
* Purpose: Returns a list of "viewable actions" for use in a SELECT SQL statement
*
* @param {String} keyName Temporary table name
*
* @return {String} sqlStmt A string of the form "( Action in ( 'doc.view',
'doc.fetch') )"
* that will be added to an SQL database SELECT
statement
*
* Documents - doc.fetch, doc.view, ll:download (doc.viewdoc occurs at the same
time as doc.view)
* Folders - ll:browse
* Compound Document - ll:browse
* Generation, alias, task list, discussion, project, poll, URL - ll:open
* XML DTD - doc.fetch, ll:download
*/
function String GetViewableList(
String keyName )
List viewableList
List inClause
String viewable
String retVal
// This list is static, so build the string the first time, then store it on
a feature
// for use later.
if ( IsUndefined( .fInClauseViewable ) )
viewableList = .GetViewableActions()
return retVal
end
sEND