XPath for Cost Based Estimating Fusion 08 Duncan Seibert www.pace2020.com | See the possibilities… Introduction • XPath, as implemented by ePace, provides the user with a tool to define a value for an operation that is custom to their shop. Typically the XPath expression is used to provide a quantity that is used as the basis for a table lookup or is multiplied by a flat rate to provide either time or material usage. www.pace2020.com | See the possibilities… Abstract • XPath is an expression language that allows the processing of values conforming to the data model defined in XQuery/XPath Data Model (XDM). The data model provides a tree representation of XML data sources as well as atomic values such as integers, strings, and booleans, and sequences that may contain both references to nodes in an XML data source and atomic values. The result of an XPath expression may be a selection of nodes from the input data sources, or an atomic value, or more generally, any sequence allowed by the data model. The name of the language derives from its most distinctive feature, the path expression, which provides a means of hierarchic addressing of the nodes in an XML tree. www.pace2020.com | See the possibilities… Session Contents 1. Determine what the calculation needs to do. 2. Determine field names. 3. Determine the data type of the result field. 4. Determine how you will cost your operation based on the data type. 5. Find your fields relative to the current object. 6. Write your expression in simple terms. 7. Write your expression in XPath. 8. Test your expression. 9. Enter your expression into ePace. 10. Use your expression. 11. Syntax. 12. Examples. www.pace2020.com | See the possibilities… 1. Determine what the calculation needs to do. • The first step is to determine what the formula needs to accomplish. • Determine which fields hold reference values and what conditions need to be met first. • Then determine the formula to arrive at the desired results. www.pace2020.com | See the possibilities… 1. Determine what the calculation needs to do. • The first step is to determine what the formula needs to accomplish. Determine which fields hold reference values and what conditions need to be met first. Then determine the formula to arrive at the desired results. • Example: To preflight a file, allow 15 minutes to review the job jacket, create a job directory on the storage server, transfer the file into the director and open the file in preflight software. Then allow 6 seconds per page to check the preflight, 10 pages per minute, 600 per hour. • Formula = .25 + (number of pages / 600). Result is in decimal hours. www.pace2020.com | See the possibilities… 2. Determine field names. • There are two way to determine the names of the fields to use. www.pace2020.com | See the possibilities… 2. Determine field names. • There are two way to determine the names of the fields to use. • One is to go to Administration -> System Configuration -> Object Model -> Object Model Browser, select the target object, click on the Fields tab and find the fields you need. • This will also show any Calculated Fields that may not appear on a screen but will make your statement easier. www.pace2020.com | See the possibilities… 2. Determine field names. www.pace2020.com | See the possibilities… 2. Determine field names. www.pace2020.com | See the possibilities… 2. Determine field names. • The other way is to open an estimate and go to details. • Go to Administration -> Toggle Debug Mode. • This will show you the names of the fields in the Object Model (dataset). • Drill into an operation like the one you will be describing, in our example a Prepress Operation. • In the label area of the field of interest there will be a name. Hover over the field to show the information. It will appear as: EstimatePrepressOp, expr: ../@quantityOrdered www.pace2020.com | See the possibilities… 2. Determine field names. www.pace2020.com | See the possibilities… 2. Determine field names. www.pace2020.com | See the possibilities… 2. Determine field names. • Example: For the preflight example, the fields we need are EstimateQuantity/@quantityOrdered, EstimateQuantity/@numPages, EstimatePart/@numSigs. • In the Object Model Browser we find that the calculated field totalPages is = @numPages * @numSigs so that is what we will use. www.pace2020.com | See the possibilities… 3. Determine the data type of the result field. • Go to the Object Model Browser, select the target object, click on the Fields tab and find the Type of the field for your result. www.pace2020.com | See the possibilities… 3. Determine the data type of the result field. • Example: EstimatePrepressOp object; quantity field has a Type of Integer www.pace2020.com | See the possibilities… 4. Determine how you will cost your operation based on the data type. • If the field is an integer, as quantity normally is, then you need to decide how to define your cost. • In a Prepress Operation size you define hours per unit. Since XPath will return integer units, to get decimal time, times should be entered as 0.01 hour per unit. www.pace2020.com | See the possibilities… 4. Determine how you will cost your operation based on the data type. • Example: Preflight operation size 99 x 99; Qty Up To 99,999,999; Hours 0.01. XPath will return an integer value for units of hundredths of an hour. www.pace2020.com | See the possibilities… 5. Find your fields relative to the current object. www.pace2020.com | See the possibilities… 5. Find your fields relative to the current object • All field references in XPath are relative to the object of the result field. www.pace2020.com | See the possibilities… 5. Find your fields relative to the current object. • Version 17 Cost Based Estimating Objects Company | Estimate | EstimatePart | EstimateQuantity | | --------------- | ---------------- | ------------------ | ------------------ | -------------- | -------------- | EstimatePrepressOp - EstimatePress - EstimateFinishingOp - EstimateOutsidePurch - EstimatePaper - EstimateInk - EstimateActivity www.pace2020.com | See the possibilities… 5. Find your fields relative to the current object. • We refer to these relationships as: EstimatePart is a child of Estimate Estimate | EstimatePart Estimate is the parent of EstimatePart EstimatePress is a sibling of EstimatePaper EstimatePress - EstimatePaper www.pace2020.com | See the possibilities… 5. Find your fields relative to the current object. • The notation for these is: ../ moves up a generation ../<object name> moves to a sibling <object name>[condition]/<object> moves down a generation. www.pace2020.com | See the possibilities… 5. Find your fields relative to the current object. • Examples: If starting location is EstimatePrepressOp, parent is EstimateQuantity which is referred to as ../ www.pace2020.com | See the possibilities… 5. Find your fields relative to the current object. • Examples: If starting location is EstimatePrepressOp, sizeDenominator (a field in company) is ../../../../@sizeDenominator The series of ../ refer, in sequence, to EstimateQuantity, then EstimatePart, then Estimate, then Company www.pace2020.com | See the possibilities… 5. Find your fields relative to the current object. • Examples: The [condition] is a way to select a field of a certain value and find data for that record. ../EstimatePress[@pressIndicator=0]/@runSizeWidth refers to the sibling object EstimatePress with a specific record where pressIndicator = 0 (primary press) and gets the runSizeWidth. www.pace2020.com | See the possibilities… 5. Find your fields relative to the current object. • Examples: To use this runSizeWidth, because we store sizes as integers, you need to divide the value by the system's size denominator so the expression would be: (../EstimatePress[@pressIndicator=0]/@runSizeWidth div ../../../../@sizeDenominator ) • 38” would be stored as 608 if your sizeDenominator is 16. www.pace2020.com | See the possibilities… 5. Find your fields relative to the current object. • Examples: To get a value from an object outside of the tree shown above you use a notation like this: (../EstimatePress[@pressIndicator=0]/press/ @gripperAllowance This would go to the sibling object, EstimatePress and select the primary press record, find the press on that record and return the gripper size. www.pace2020.com | See the possibilities… 5. Find your fields relative to the current object. • Examples: To get a value from an object outside of the tree by using a direct pointer, use syntax like this: /InventoryItem[@id='123Test']/@unitPrice If the object is a singleton (single record object like many setup objects), then the record selection is not necessary: /EstimateSetup/@paperRoundingMode www.pace2020.com | See the possibilities… 6. Write your expression in simple terms. • Formula from Step 1 = .25 + (number of pages / 600) • (¼ hour + ( sum(numPages for all parts) / 600)) * 100 • Remember, units are integers so to get 1/100 of an hour we must multiply by 100 to get units of .01 hours. 67 units times .01 hours = .67 hours www.pace2020.com | See the possibilities… 7. Write your expression in XPath. • (.25 + (sum(../../../EstimatePart/@totalPages) div 600)) * 100 • Note: To sum a value you must go to the parent and then sum the children. Here we go up to the Estimate and then refer to the EstimateParts. This will give us the total number of pages in all parts of the Estimate. www.pace2020.com | See the possibilities… 8. Test your expression. • Go to a sample estimate and manually calculate the result you want the system to return. • In our example, we have a large perfect bound book of 1,566 pages. • 1566 @ 600/hour = 2.61 hours + .25 = 2.86 • 2.86 hours * 100 = 286 units www.pace2020.com | See the possibilities… 8. Test your expression. • Go to a sample estimate and get the object number as a data starting point. • Example: Go to your estimate detail Prepress tab and hover over a prepress operation drilldown icon. At the bottom of the browser window will appear a URL like: http://epace.samplecompany.com/estimating/object/ EstimatePrepressOp/detail/5987 www.pace2020.com | See the possibilities… 8. Test your expression. www.pace2020.com | See the possibilities… 8. Test your expression. • Go to Administration -> System Tools -> XPath Evaluator. • In Object Type enter the result's object. • The primary key is the number at the end of the URL. • The data type is from step 3. • The Expression is the one you have written. • Hit the Evaluate button and the Result should produce the value you expect. www.pace2020.com | See the possibilities… 8. Test your expression. www.pace2020.com | See the possibilities… 9. Enter your expression into ePace. • Go to Administration -> System Setup -> Estimating -> Misc Setup -> Estimate Expression Setup. • Add New Record. • Enter a name, calculation type and copy your expression into the appropriate area, Expression Prepress or Expression Finishing. www.pace2020.com | See the possibilities… 9. Enter your expression into ePace. www.pace2020.com | See the possibilities… 10. Use your expression. • Go to either a Prepress Workflow or a Finishing Operation. • In the Quantity Calc Method select XPath Expression • Choose your new expression in the Estimate Expression field www.pace2020.com | See the possibilities… 10. Use your expression. www.pace2020.com | See the possibilities… 11. Syntax • ePace uses XPath 1.1 with custom extensions. If you get an XPath book, it may have version 2.0 expressions which will not be understood by ePace. www.pace2020.com | See the possibilities… 11. Syntax • • Any time an expression uses more than one operator, it is necessary to know what precedence is used. For XPath, this is (highest to lowest): ( ) 'grouping' [ ] 'filter' 'unary minus' * div mod 'multiplication division modulus' + - 'addition subtraction' = != < <= > >= 'relational (comparison)' | 'union' not 'negation' and 'conjunction' or 'disjunction' Operators at the same precedence level are always evaluated left-to-right. Parentheses can be used to force the expression to evaluate in a different order than the default. www.pace2020.com | See the possibilities… 11. Syntax • Here are the some of the syntax statements that are currently available: Comparison operators: = 'equal to' != 'not equal to' > 'greater than' >= 'greater than or equal to' < 'less than' <= 'less than or equal to' Contains 'list of values contains test value' not 'reverses the true/false value of its argument' www.pace2020.com | See the possibilities… 11. Syntax • Here are the some of the syntax statements that are currently available: Conditional Statement: iif(<value> condition <value>, <result>, <else result>) this may be nested www.pace2020.com | See the possibilities… 11. Syntax • Here are the some of the syntax statements that are currently available: Arithmetic Statements: + 'plus' - 'minus' * 'multiply by' div 'divide by' mod 'modulus' null-to-zero 'if @addHours returns [null], null-to-zero(@addHours) returns 0' www.pace2020.com | See the possibilities… 11. Syntax • Here are the some of the syntax statements that are currently available: Summary Statements: sum(<value list>) floor(<value>) result is rounded down toward negative infinity (negative numbers increase in absolute value) ceiling(<value>) result is rounded up toward positive infinity round(<value>) result is rounded. < .5 rounds down >= .5 rounds up count(<value>) result is number of records www.pace2020.com | See the possibilities… 11. Syntax • Here are the some of the syntax statements that are currently available: String: number(<string value>) 'result is a number. Use to convert string values before math' contains(string_1, string_2) 'returns true if string_1 contains string_2' concat(<string_1>, <string_2>, <string_3>) 'concatenates strings' starts-with(<string_1>,<string_2>) 'returns true if string_1 starts with string_2, case sensitive' string(<value>) 'returns value as string, used to return a number as a string' www.pace2020.com | See the possibilities… 11. Syntax • Here are the some of the syntax statements that are currently available: String: string-length(<string>) 'returns number of characters in the string' substring(<string>,offset,length) 'returns a substring of <length> characters starting at <offset> ' substring-after(<string_1>,<string_2>) 'returns the part of string_1 that follows the 1st occurance of string_2 ' substring-before(<string_1>,<string_2>) 'like substring-after but returns the part before' current-date() 'returns the current date and can be used as a comparison in a iif statement' current-user() 'returns the current user' www.pace2020.com | See the possibilities… 11. Syntax • Here are the some of the syntax statements that are currently available: Notes: There is no min or max function that will compare different fields so use this format: To return minimum value of x or y use iif (x < y,x,y) The min and max functions in XPath only return the minimum or maximum value within a field. There is no case statement so use nested iif statements. There is no variable declaration. All statements must be included in a single statement. You cannot return multiple values. The first expression that returns a true in a nested iif statement returns the value and ends the iif. It is like writing: if x=y then x end if, if y=z then z end if www.pace2020.com | See the possibilities… 12. Examples • For a simple price list: 1-10 @ 1.75 11-25 @ 1.50 26 and over @ 1.25 ((iif (../@quantityOrdered <= 10, (../@quantityOrdered * 1.75, iif (../@quantityOrdered >= 11 and ../@quantityOrdered <= 25, (../@quantityOrdered * 1.50, (../@quantityOrdered * 1.25)))) www.pace2020.com | See the possibilities… 12. Examples • Final Size in 8.5 x 11 equivalents ((../../@finalSizeHeight div (../../../../@sizeDenominator)) * (../../@finalSizeWidth div (../../../../@sizeDenominator)) * 93.5) www.pace2020.com | See the possibilities… 12. Examples • Banner Hemming - result is final size perimeter feet (((../../@finalSizeHeight div (../../../../@sizeDenominator * 12)) + (../../@finalSizeWidth div (../../../../@sizeDenominator * 12))) * 2 * ../@quantityOrdered www.pace2020.com | See the possibilities… 12. Examples • Grommets - for each part, put a grommet along each side. Max span = 4 feet, minimum 4 per piece (each corner) ../@quantityOrdered * (ceiling((../../@finalSizeHeight div (../../../../@sizeDenominator * 12)) div 4 ) * 2 + ceiling((../../@finalSizeWidth div (../../../../@sizeDenominator * 12)) div 4 ) * 2) www.pace2020.com | See the possibilities… 12. Examples • Laminating - 28" wide - run inches. If the sheet width is over 28" it has to run short edge into the laminator, if under 28" then long edge. (iif((../EstimatePress[@pressIndicator=0]/@runSizeWidth div (../../../../@sizeDenominator)>28, ((../EstimatePress[@pressIndicator=0]/@runSizeWidth div (../../../../@sizeDenominator)), ((../EstimatePress[@pressIndicator=0]/@runSizeHeight div (../../../../@sizeDenominator)) )) * ../@sheetsOffPress www.pace2020.com | See the possibilities… 12. Examples • Laminating - 28" wide - run inches - add 20% if under 7 point or over 16 point or quantity is under 1000. If the sheet width is over 28" it has to run short edge into the laminator, if under 28" then long edge. iif(../EstimatePaper[1]/paperWeight/@caliper < .007,1.2, iif(../EstimatePaper[1]/paperWeight/@caliper > .016,1.2, iif(../@sheetsOffPress < 1000,1.2,1))) * (iif((../EstimatePress[@pressIndicator=0]/@runSizeWidth div (../../../../@sizeDenominator)>28, ((../EstimatePress[@pressIndicator=0]/@runSizeWidth div (../../../../@sizeDenominator)), ((../EstimatePress[@pressIndicator=0]/@runSizeHeight div (../../../../@sizeDenominator)) )) * ../@sheetsOffPress www.pace2020.com | See the possibilities… 12. Examples • Laminate material per side in MSI (../EstimatePress[@pressIndicator=0]/@runSizeWidth div ../../../../@sizeDenominator) * (../EstimatePress[@pressIndicator=0]/@runSizeHeight div ../../../../@sizeDenominator) * ../@sheetsOffPress div 1000 www.pace2020.com | See the possibilities… 12. Examples • To apply a calculation to the first part of an estimate iif(../../@id = min(../../../EstimatePart/@id),<value or expression>,0) • For the Preflight Operation this would be iif(../../@id = min(../../../EstimatePart/@id), (.25 + (sum(../../../EstimatePart/@totalPages) div 600)) * 100,0) www.pace2020.com | See the possibilities… 12. Examples • To get the total material cost for an Estimate Part, from a prepressOp or finishingOp for Canadian tax • This shows 2 conditions, EstimateActivity.estimateQuantity = (../@id) AND EstimateActivity.hours = 0 • Return 12% of the total material cost on the part 0.12 * sum(../EstimateActivity[@estimateQuantity=(../@id) and null-to-zero(@hours) =0]/@cost) www.pace2020.com | See the possibilities… Thank you for attending www.pace2020.com | See the possibilities…