= MultiClassification = <> = Requirements = * unlimited number of classifications for one document (task) * unlimited number of assigned aspect values in one aspect = Solution = Separate '''[[Classifications]]''' records are used for every classification which is set. No empty records used, a document has as many '''[[Classifications]]''' records as many classifications are set. = Issues = 1. How to handle auto-classification from the drawing code? E.g. do you create a new (additional) classification, or (all?) replace existing classifications? == Detail == No problems determined until now. A smarty loop goes through all '''[[Classifications]]''' records to display them line to line. == New/Update == For '''every''' aspects defined in table '''[[Aspects]]''' at least one line appears even if it is empty. At the right side of all line there is a ''new'' button to open additional line for entering classification in the same aspect, and a ''delete'' button to remove that classification. If there is no more classification in an aspect (after deleting the last one) an empty line appears. || aspect X || code || AX_L1_0 || AX_L2_0 || ... || from - to || ''new/delete'' || || || code || AX_L1_1 || AX_L2_1 || ... || from - to || ''new/delete'' || == Search == Searching is a performance issue. Because of all '''[[Classifications]]''' are different records a large joins could be used to perform searching, which can be very slow. The following optimization steps will be implemented: 1. determine first the number of '''[[Classifications]]''' records involved in one aspect value search 1. perform a nested select with the smallest records set in the inner most level to limit the involved records to the minimum. The result should be saved into a temp table in the database. This temp table should be deleted at every new search or list. 1. read DrawingsRevisionsJoinSet with the id set got from the select above E.g.: assumed that we are looking for documents with aspect codes 22, 345 and 42, the steps above: {{{ 1. select code, count(*) from Classifications where code in ( '22', '345', '42' ) order by 2; +------+----------+ | code | count(*) | +------+----------+ | 345 | 10 | | 42 | 234 | | 22 | 1410 | +------+----------+ 2. select objectId from Classifications where objectTypeId = ... and code = '22' and object Id in ( select objectId from Classifications where objectTypeId = ... and code = '42' and object Id in ( select objectId from Classifications where objectTypeId = ... and code = '345' ) ) }}} == List == Displaying of classifications in a document list is also a performance issue. To be able to display classification information quickly a static database table will be used to store some information about classified documents: '''AspectCache Table''' || Object Id || Object Type ||<-7> aspect 0 ||...||<-7> aspect 9 || || || || min code || max code || full code || hoverText || count || from || to ||...|| min code || max code || full code || hoverText || count || from || to || There is one record for one object (Drawing, Task). The field ''full code'' is a comma separated list of all aspect values in the appropriate aspect assigned to the object. On a document list with classifications the ''min code'', ''max code'' can appear according the sorting order. On a non-sorted list the ''full code'' can appear or it can be a site setting parameter. '''Issue''' The AspectCache table can be updated: 1. database procedures on regularly basis (cron?) 1. fired by triggers on the tables Drawings, Comments and Classifications 1. by application transactions '''Listing Multiple Classification Values''' Three cases are possible: 1. One classification, with a single from-to pair (E.g. one chainage range on a single alignment) * Display: '''Code x->y''' -- or code only, with no values? * Hover: '''Name x->y''' 1. Multiple classifications on same classification, with different from-to pairs (e.g. multiple chainage ranges on a single alignment) * Display: '''Code min-x -> max-y...''' * Hover: '''Name min-x -> max-y (multiple)''' 1. Multiple classifications on different classification, with different from-to pairs (e.g. multiple chainage ranges on multiple alignments) * Display: '''Code-mix, Code-max...''' -- Or can we display something more sensible? * Hover: '''Name-min, Nam-max...(multiple)''' We don't need to make a decision now, as the display only changes the update that fills the aspect cache table. = Implementation Notes = == Meta Generation == 1. Javascript core stored at: '''app/js/cascadingSelect.js''', which is installed in '''pub/js''' (by app/install/setup.sh) 1. Implemented in '''lib/meta/aspect''', which calls: '''app/model/AspectValuesSet->generateJavascript( $jsPath, $phpPath )''' 1. Generates file: '''var/{site}/{instance}/config/AspectConfig.php''' - defines the max number of levels in each classification aspect. 1. Generates files: '''pub/asp_cfg_{aspectId}.js''' - Calls to cascadingSelect.js with classification codes/names. == HTML Generation == Generation of the form HTML is performed by the custom Smarty tag: '''html_aspect_select''', implemented in: * lib/smarty/plugin/function.html_aspect_select.php HTML input fields names (output by html_aspect_select): * '''code_{i}''' - Entry field for entering a ''code'' value, causes selects to change. * '''asp_sel_{i}_{j}''' - Select field(s), j=1..maxLevel. Visible/hidden depending on what is selected. * '''asp_val_fr_{i}_{j}''' - ''From'' value field. Visible/hidden depending on configuration and what is selected. Enabled by the current selected value and AspectValues.dataTypeId. * '''asp_val_to_{i}_{j}''' - ''To'' value field. Visible/hidden depending on configuration and what is selected. Enabled by the current selected value and AspectValues.fromToFlag. * '''levelCode_{i}''' - Hidden field which contains the AspectValues.levelCode for the currently selected value. The first select is always visible. * '''aspectValueId_{i}''' - Hidden fields which contains the AspectValues.aspectValueId for the currently selected value. Generates the following for a single level aspect (example for aspectId=3): {{{ <-- new <-- new }}} Generates the follow for a two-level select (for aspectId=2): {{{ <-- new <-- new <-- new <-- new }}} Note that the numbering in the HTML is +1, compared to the aspectId value. == Data Values == Data values are optional text entry fields that are associated an the aspect value. En example usage is for alignments with chainages, where you could have an aspect consisting of a series of alignments with from/to values representing chainage ranges. 1. Display of data value fields is controlled by two fields: 1. '''!AspectValues.dataTypeId''' - Controls is ''from/to''fields are generated. 1. '''!AspectValues.fromToFlag''' - Controls if a ''to'' HTML text entry field is generated. 1. '''!AspectValues.dataTypeId''' is a foreign key to '''!ClassificationDataTypeRef.dataTypeId''' The following data types are available: * 1 = Boolean - '''Not implemented yet''' * 2 = Integer * 3 = Float * 4 = Date * 5 = Text - '''Not implemented yet''' * 6 = URI - '''Not implemented yet''' * 7 = None - '''Not implemented yet''' == Cascading Select Java Script == === Current Status (2009-12-01) === Sample test code that implements from/to values: [[attachment:cselect7-20091201.tgz]] * This tar file includes test .html and .jp files for demonstrating the display and hiding of from/to value fields. '''List of files changed''' (check-in of 2009-12-01) for implementation of from/to fields: Use this list if there is a major error and you need to do a roll-back. 1. app/js/cascadingSelect.js 1. app/model/AspectRefSet.php 1. app/model/AspectValuesSet.php 1. lib/smarty/plugins/function.html_aspect_select.php '''Status:''' 1. Simple testing with recent MET production data (which doesn't have any from/to classifications configured, appears to work. * Able to save/update documents using the existing classifications * Tested a simple from/to case (by patching the configuration, on code '17-C-02': {{{ update AspectValues set dataTypeId=2, fromToFlag=1 where levelCode='002_008_001'; }}} 1. Input field capture and validation is not performed at all. Meaning you lose the from/to value when you press ''Next''. '''Suggested Future testing:''' 1. Check three or four level classifications 1. Check three level classifications where the first two levels have from/to fields as well. And make sure the values at the different levels do not get mixed up. '''Next Steps:''' 1. Decide how to perform validation (based on the dataTypeId) and how to report validation errors. Ideally the validation should be performed directly in the Javascript (but that is probably a big job). === Javascript Entry points === ==== Initialization ==== Initialization - Is performed in the generated Javascript code files '''pub/asp_cfg_{aspeciId}.js''' 1. For each aspect there is a separate generated file which initializes a set of menus that defines the entire ''classification tree'' for that aspect. 1. '''new selectSet'''( formName, aspectId, unselectedText, debugFlag ) * formName: HTML form name * aspectId: aspectId from database * unselectText: Text for the ''unselected'' option, typically ''- Select -'' * debugFlag: set to true to enable debug popups, and display of hidden fields. 1. '''new Menu'''( classId, selectSet, level ) * classId - a unique id over the entire selectSet * selectSet - the select set to which this menu belongs * level - the cascade level (0=top level, 1=next level, ..) 1. '''addItem(''' valueId, dataTypeId, fromToFlag, levelCode, code, label, subMenu ) * valueId * dataTypeId - from !AspectValues.dataTypeId * fromToFlag - from !AspectValues.fromToFlag * levelCode - from !AspectValues.levelCode * code - The code value displayed in the select menu (''don't think that this is actually used!'') * label - the label text, which normally includes the code value (e.g. 'CODE - Label Text') * subMenu - set to the Menu of the sub-menu, if this item has a sub-menu (optional) ==== Run-Time Entry Points ==== 1. '''!SelectSet.init()''' - called when the screen is first displayed, and when the code field is changed. Sets the select states and display of from/to fields based on the text value in the code field. 1. '''onchange='!SelectSet.update( this )''' - Called when a select menu is changed, the ''this'' parameter points to the HTML select element. Displays/hides (all) select inputs and from/to fields based on the select that was changed. === cascadingSelect.js Classes === 1. '''!MenuItem''' - Implements a single menu item 1. '''Menu''' - Implements a set of ''MenuItems'' 1. '''Select''' - Implements an HTML select, includes a set of Menus (in the ''items'' variable) 1. '''!SelectSet''' - Implements a set of ''Selects'', e.g. a complete cascading select for a single aspect. Functions are named using the convention '''{className}_{functionName}''', e.g. '''SelectSet_init''' or '''Menu_addItem'''.