Batch assigning attributes of a category

Hi,

I have a list of object ids all of which are tagged using the same category.

I now want to assign a certain attribute value of a Text: Popup (drop down) field to all of those object ids.

Any idea/hint how to do that with OScript?

 Thanks a lot

 Juergen

Comments

  • the attrdata data structure is the commonly used one.The psuedo code follows this

     

    With Each Objectid

    ask NodeCategoriesGet

    Isolate the category you want manipulated

    change the values using the attribute display name key method

    dbput to save changes

    end with

    repeat for other objectid's

    https://knowledge.opentext.com/knowledge/cs.dll?func=ll&objId=20007573&objAction=viewincontainer

    There is another way that I found out when reseraching something but I am not able to rememebr that correctly now.It was a tad bit more easy once you understand it.

    attched is a utility script I created so that given a dataid and a category name and the values one could manipulate it.I put that as a utils script you can look at its implementation if that helps

    https://knowledge.opentext.com/knowledge/cs.dll?func=ll&objId=17996016&objAction=download&viewType=1

    I used the simple OT code I found on the KB for different types of categories not completely checked or thorough by any means

  • Thanks a lot for your post! It helped big style to adopt it to my needs, I attach the first version of the code to this post, perhaps it helps someone.

    Juergen

    ---8<---</p>

    /* description *//*This script loads a list of object ids from a text fileand adds a Text Popup attribute entry of a given category*//* configuration data */Assoc aChangeDataaChangeData.( 'sCategory' ) = "[name of category]"aChangeData.( 'sAttribute' ) = "[name of attribute]"aChangeData.( 'sAttributeValue' ) = "[new value for dropdown list]"String	pathRoot = "p:/ath/"String	fileInput = pathRoot + "input.txt"String	fileError = pathRoot + "error.log"String	fileSuccess  = pathRoot + "success.log"/* code */File	hdInput = File.Open( fileInput, File.ReadMode )File	hdError = File.Open( fileError, File.WriteMode )File	hdSuccess = File.Open( fileSuccess, File.WriteMode )String	s = ""Integer nidAssoc rvif ( ! IsError( hdInput ) )	// OT construct to loop through input file	for ( s = File.Read( hdInput ); s != File.E_Eof; s = File.Read( hdInput ) )		nid = Str.StringToInteger( s )		if( isDefined( nid ) )			// call routine to change attribute value			rv = setAttributeForObjectId( this.fprgCtx, nid, aChangeData )		else			rv.OK = FALSE			rv.errMsg = s + " is no valid object id format."		end		// log result		if( rv.OK )			File.Write( hdSuccess, Str.ValueToString( nid ) )		else			File.Write( hdError, rv.errMsg )		end	end	File.Close( hdInput )	File.Close( hdError )	File.Close( hdSuccess )end// check if a value exists in listfunction Boolean bHasValue( List lsValues, String sValue )	Integer c = Length( lsValues )	Integer ix	String s	for ix = 1 to c		s = lsValues[ ix ]		if( s == sValue )			return true		end	end	return falseendfunction Assoc setAttributeForObjectId( Object prgCtx, Integer nid, Assoc aChangeData )	Dynamic		result	Dynamic		aCategoryData	List		llsCategories	Boolean		found, bFoundAttribute	List		lsCategories	Integer		ixCategory = 0	Integer		nidAttribute	Dynamic		category,aAttributes	List		lsUpdatedItems	String		sCategory = aChangeData.( 'sCategory' )	String		sAttributeValue = aChangeData.( 'sAttributeValue' )	String		sAttribute = aChangeData.( 'sAttribute' )	String		sid = Str.ValueToString( nid )	Assoc retVal	retVal.OK=TRUE	retVal.errMsg=""	//Get the keys of the assoc	Dynamic  docNode = DAPI.GetNodeById(prgCtx.DSession().fSession, DAPI.BY_DATAID, nid)	if isError(docNode)		retVal.OK = FALSE		retVal.errMsg = sid + " is no valid node"		return retVal	end	// Create a new attrData structure to return the info in	aCategoryData = $LLIApi.attrData.New( prgCtx,  nid, docNode.pVersionNum )	// Get the data from the database for this object (All categories)	result = aCategoryData.DBGet()	if !result.OK		retVal.OK = FALSE		retVal.errMsg = sid + " has no categories attached"		return retVal	end	if result.OK		// Now get the list of all categories attached		llsCategories = aCategoryData.AttrGroupNameList()		// We now must look for the category specified to see if it was returned		found = false		bFoundAttribute = false		for lsCategories in llsCategories			If lsCategories[ 2 ] == sCategory				// The category specified was found. 				found = true				Integer ixCategory = 0				// Now we need to extract the id of the category				for category in aCategoryData.fDefinitions					ixCategory = ixCategory + 1					if category.DisplayName == sCategory						// ixCategory now holds id of target category						// now lets look at the target attribute						for aAttributes in aCategoryData.fDefinitions[ixCategory].Children							if aAttributes.DisplayName == sAttribute								bFoundAttribute = true								// now we are in the target attribute, lets get its id								nidAttribute = aAttributes.ID								// get valid values for this attribute								List lsValidValues = aAttributes.ValidValues								// if new attribute value is valid update the data object accordingly								if( bHasValue( lsValidValues, sAttributeValue ) )									List lsItems = aCategoryData.fData[ixCategory].Values[1].(nidAttribute).Values									Integer cItems = Length( lsItems )									if( cItems == 1 && IsUndefined( lsItems[ 1 ] ) )										// no items set, set new one										lsUpdatedItems = { sAttributeValue }									else										if( bHasValue( lsItems, sAttributeValue ) )											retVal.OK = FALSE											retVal.errMsg = sid + " attribute value already exists"											return retVal										else											// items already exist, add new one											lsUpdatedItems = { @lsItems, sAttributeValue }										end									end									// assign new attribute value to data object									aCategoryData.fData[ixCategory].Values[1].(nidAttribute).Values = lsUpdatedItems								else									retVal.OK = FALSE									retVal.errMsg = sid + " " + sAttributeValue + " is no valid value of attribute " + sAttribute									return retVal								end							end						end						if( bFoundAttribute )							// Update node with new data object							result = aCategoryData.DBPut()							break						else							retVal.OK = FALSE							retVal.errMsg = sid + " " + sAttribute + " is no attribute in category " + sCategory							return retVal						end					end				end				if !result.OK					retVal.OK = FALSE					retVal.errMsg = sid + " updated failed, system says: " + result.APIError					return retVal				end			end		end		if !found			// the category is not in this node			retVal.OK = FALSE			retVal.errMsg = sid + " has no category called " + sCategory + " attached"			return retVal		end	end	return retValend
  •  

    May I ask you an additional question? I got a request to correct a typo in a Text/Popup attribute value of a category.
     
    Now if I simply change the text value of the attribute in the category and apply that new version to my objects all previous assignments will be completely lost on those objects.
     
    Hence I was thinking of the following approach:
    * Fetch all object ids that are tagged with old attribute value (with typo) from LLATTRDATA table.
    * Add a new attribute value in (typo corrected) to attribute of category
    * Remove old attribute value (with typo) to attribute of category
    * Upgrade categories 
    * Run above script to assign the new attribute value
     
    Is there a more elegant way not including a category upgrade?
     
     Thanks,
     Juergen
     
  • thank you for sharing this!

     

    From: eLink Entry: Content Server Builder Forum [mailto:[email protected]]
    Sent: Thursday, March 29, 2012 3:59 AM
    To: eLink Recipient
    Subject: Batch assigning attributes of a category

     

    Batch assigning attributes of a category

     

    Posted by[email protected] (Riemer, Juergen) On 03-29-2012 03:53

     

     

    Thanks a lot for your post! It helped big style to adopt it to my needs, I attach the first version of the code to this post, perhaps it helps someone.

    Juergen

    ---8<---

     
    /* description */
    /*
    This script loads a list of object ids from a text file
    and adds a Text Popup attribute entry of a given category
    */
     
    /* configuration data */
     
    Assoc aChangeData
    aChangeData.( 'sCategory' ) = "[name of category]"
    aChangeData.( 'sAttribute' ) = "[name of attribute]"
    aChangeData.( 'sAttributeValue' ) = "[new value for dropdown list]"
    String  pathRoot = "p:/ath/"
    String  fileInput = pathRoot + "input.txt"
    String  fileError = pathRoot + "error.log"
    String  fileSuccess  = pathRoot + "success.log"
     
     
    /* code */
     
    File    hdInput = File.Open( fileInput, File.ReadMode )
    File    hdError = File.Open( fileError, File.WriteMode )
    File    hdSuccess = File.Open( fileSuccess, File.WriteMode )
    String  s = ""
    Integer nid
    Assoc rv
     
    if ( ! IsError( hdInput ) )
            // OT construct to loop through input file
            for ( s = File.Read( hdInput ); s != File.E_Eof; s = File.Read( hdInput ) )
                   nid = Str.StringToInteger( s )
                   if( isDefined( nid ) )
                           // call routine to change attribute value
                           rv = setAttributeForObjectId( this.fprgCtx, nid, aChangeData )
                   else
                           rv.OK = FALSE
                           rv.errMsg = s + " is no valid object id format."
                   end
                   // log result
                   if( rv.OK )
                           File.Write( hdSuccess, Str.ValueToString( nid ) )
                   else
                           File.Write( hdError, rv.errMsg )
                   end
            end
            File.Close( hdInput )
            File.Close( hdError )
            File.Close( hdSuccess )
    end
     
    // check if a value exists in list
    function Boolean bHasValue( List lsValues, String sValue )
     
            Integer c = Length( lsValues )
            Integer ix
            String s
     
            for ix = 1 to c
                   s = lsValues[ ix ]
                   if( s == sValue )
                           return true
                   end
            end
            return false
    end
     
    function Assoc setAttributeForObjectId( Object prgCtx, Integer nid, Assoc aChangeData )
     
            Dynamic        result
            Dynamic        aCategoryData
            List           llsCategories
            Boolean        found, bFoundAttribute
            List           lsCategories
            Integer        ixCategory = 0
            Integer        nidAttribute
            Dynamic        category,aAttributes
            List           lsUpdatedItems
     
            String         sCategory = aChangeData.( 'sCategory' )
            String         sAttributeValue = aChangeData.( 'sAttributeValue' )
            String         sAttribute = aChangeData.( 'sAttribute' )
            String         sid = Str.ValueToString( nid )
     
            Assoc retVal
            retVal.OK=TRUE
            retVal.errMsg=""
     
            //Get the keys of the assoc
            Dynamic  docNode = DAPI.GetNodeById(prgCtx.DSession().fSession, DAPI.BY_DATAID, nid)
     
            if isError(docNode)
                   retVal.OK = FALSE
                   retVal.errMsg = sid + " is no valid node"
                   return retVal
            end
     
            // Create a new attrData structure to return the info in
            aCategoryData = $LLIApi.attrData.New( prgCtx,  nid, docNode.pVersionNum )
     
            // Get the data from the database for this object (All categories)
            result = aCategoryData.DBGet()
            if !result.OK
                   retVal.OK = FALSE
                   retVal.errMsg = sid + " has no categories attached"
                   return retVal
            end
     
            if result.OK
                   // Now get the list of all categories attached
                   llsCategories = aCategoryData.AttrGroupNameList()
     
                   // We now must look for the category specified to see if it was returned
                   found = false
                   bFoundAttribute = false
                   for lsCategories in llsCategories
                           If lsCategories[ 2 ] == sCategory
     
                                   // The category specified was found. 
                                   found = true
                                   Integer ixCategory = 0
     
                                   // Now we need to extract the id of the category
                                   for category in aCategoryData.fDefinitions
                                          ixCategory = ixCategory + 1
                                          if category.DisplayName == sCategory
                                                  // ixCategory now holds id of target category
                                                  // now lets look at the target attribute
                                                  for aAttributes in aCategoryData.fDefinitions[ixCategory].Children
                                                         if aAttributes.DisplayName == sAttribute
                                                                 bFoundAttribute = true
                                                                 // now we are in the target attribute, lets get its id
                                                                 nidAttribute = aAttributes.ID
                                                                 // get valid values for this attribute
                                                                 List lsValidValues = aAttributes.ValidValues
                                                                 // if new attribute value is valid update the data object accordingly
                                                                 if( bHasValue( lsValidValues, sAttributeValue ) )
                                                                         List lsItems = aCategoryData.fData[ixCategory].Values[1].(nidAttribute).Values
                                                                         Integer cItems = Length( lsItems )
                                                                         if( cItems == 1 && IsUndefined( lsItems[ 1 ] ) )
                                                                                // no items set, set new one
                                                                                lsUpdatedItems = { sAttributeValue }
                                                                         else
                                                                                if( bHasValue( lsItems, sAttributeValue ) )
                                                                                        retVal.OK = FALSE
                                                                                        retVal.errMsg = sid + " attribute value already exists"
                                                                                        return retVal
                                                                                else
                                                                                        // items already exist, add new one
                                                                                        lsUpdatedItems = { @lsItems, sAttributeValue }
                                                                                end
                                                                         end
                                                                         // assign new attribute value to data object
                                                                         aCategoryData.fData[ixCategory].Values[1].(nidAttribute).Values = lsUpdatedItems
                                                                 else
                                                                         retVal.OK = FALSE
                                                                         retVal.errMsg = sid + " " + sAttributeValue + " is no valid value of attribute " + sAttribute
                                                                         return retVal
                                                                 end
                                                         end
                                                  end
                                                  if( bFoundAttribute )
                                                         // Update node with new data object
                                                         result = aCategoryData.DBPut()
                                                         break
                                                  else
                                                         retVal.OK = FALSE
                                                         retVal.errMsg = sid + " " + sAttribute + " is no attribute in category " + sCategory
                                                         return retVal
                                                  end
                                          end
                                   end
     
                                   if !result.OK
                                          retVal.OK = FALSE
                                          retVal.errMsg = sid + " updated failed, system says: " + result.APIError
                                          return retVal
                                   end
                           end
                   end
                   if !found
                           // the category is not in this node
                           retVal.OK = FALSE
                           retVal.errMsg = sid + " has no category called " + sCategory + " attached"
                           return retVal
                   end
            end
     
            return retVal
     
    end

    [To post a comment, use the normal reply function]

    Topic:

    Batch assigning attributes of a category

    Forum:

    Content Server Builder Forum

    Content Server:

    Knowledge Center

     

  • I think not...

    For the existing objects with the typo if you change the value of the category:attribute data that would suffice.The minute you change the category version on the definition all those nodes sitting with old category data will demand/show that we all need to be upgraded right now.

    In a similar situation where the doccontrollers changed the cat:att to include a mandatory value and gave me this requirement

    1)Old category att data on nodes must be maintained

    2)The node should be upgraded and a default value should be given to the new mandatory attribute that was given.OT Upgrade will skip those objects if it cannot find a mandatory value.Defining it at a folder level or the category definition will destroy all the old data in it. I was in a rich soup.

    3)each node encountered had at least 3 more categories on it that needed to be maintained.

    In the end I was able to accomodate the needs with my own implementation of a "nodecrawler" .

    Further my peeking in the oscript layer made me understand that llattrblobdata was the real deal and llattrdata was reconstructed based on that.

    Hopefully in this age of webreports something of the same could be acheived albeit using their tag programming.oscript is going to be faster as you reach the engine directly.

     

  •  

    Hi Appu,
     
    may I ask an additional question? :)
    I successfully run this script already; I always had to upgrade the categories afterwards for another reason which was great because without the upgrade of the entire category the items were properly tagged with the new attribute value, however, you couldn't search for them. Only the category upgrade "re-indexed" them.
    I'd like to decouple this.
    What I IMHO need to do is to run the "upgrade category" function on all of those object ids. Do you have a hint which methods to call to perform a category upgrade for a single object id?
     
     Thanks so much,
     Juergen
     
  • You only need to do this

    Assoc myResult=attrData.Upgrade(myDefID)(see the attrdata class method)

    However

    The upgrade category is a single function but it does not work if the data is not put right.
    I can tell you with an e.g Assume a category with this spec {12345,5} is applied to some nodes
    of your livelink.For simplicity's sake I will say that there are 40 folders and 60 documents with this data.
    Assume you now changed the category so it is now {12345,6} and added a mandatory value.
    If you try to upgrade the said Id's 40 will get upgraded and 60 will get rejected saying it needs a mandatory value filled.If you hardcode the manadatory value it will work but it will replace the existing values of the original attributes i don't think it will "merge".

    Are there ways around it Yes very contrived

    1. On a livelink server where your code is running take out the mandatoriness insistence of categories which is what you do by going "System Administration->Configure Attribute Value Requirements'.This is the flag that makes documents insist on mandatory and so on.After you upgrade you can put it back.This idea did not fly with my customer when I was developing.I am not sure if this setting is a per instance setting or in KINI if it is in KINI then all your instances will behave like this.

    Attached is a utilty script from my program it will not work as such but as you can see when I sense that I can't upgrade a category unless I fill in the manadatory value...

    https://knowledge.opentext.com/knowledge/cs.dll?func=ll&objaction=overview&objid=24681779

     

     

  • Hi Appu,

    Thanks a lot for your detailed information! I will have a look at your code, great!

     Juergen