MultiClassification
Contents
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
- 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:
determine first the number of Classifications records involved in one aspect value search
- 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.
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 |
aspect 0 |
... |
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:
- database procedures on regularly basis (cron?)
- fired by triggers on the tables Drawings, Comments and Classifications
- by application transactions
Listing Multiple Classification Values
Three cases are possible:
- 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
- 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)
- 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
Javascript core stored at: app/js/cascadingSelect.js, which is installed in pub/js (by app/install/setup.sh)
Implemented in lib/meta/aspect, which calls: app/model/AspectValuesSet->generateJavascript( $jsPath, $phpPath )
Generates file: var/{site}/{instance}/config/AspectConfig.php - defines the max number of levels in each classification aspect.
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):
<input name="code_4" type="text" size="6" value="" onchange="asp_4.init();" tabindex=1/> <select name="asp_sel_4_0" size="1" onchange="asp_4.update(this);" tabindex=2></select> <input name="asp_val_fr_4_0" size="10" style='display=none'> <-- new <span id='asp_val_to_4_0' style='display:none'> <-- new → <-- new <input name="asp_val_fr_4_0" size="10" style='display=none'> <-- new </span> <-- new <input name="levelCode_4" style="display:none;"> <input name="aspectValueId_4" style="display:none;">
Generates the follow for a two-level select (for aspectId=2):
<input name="code_3" type="text" size="6" value="" onchange="asp_3.init();" tabindex=1/> <select name="asp_sel_3_0" size="1" onchange="asp_3.update(this);" tabindex=2></select> <input name="asp_val_fr_3_0" size="10" style='display=none'> <-- new <span id='asp_val_to_3_0' style='display:none'> <-- new → <-- new <input name="asp_val_fr_3_0" size="10" style='display=none'> <-- new </span> <-- new <select name="asp_sel_3_1" size="1" onchange="asp_3.update(this);" tabindex=2></select> <input name="asp_val_fr_3_1" size="10" style='display=none'> <-- new <span id='asp_val_to_3_1' style='display:none'> <-- new → <-- new <input name="asp_val_fr_3_1" size="10" style='display=none'> <-- new </span> <-- new <input name="levelCode_3" style="display:none;"> <input name="aspectValueId_3" style="display:none;">
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.
- Display of data value fields is controlled by two fields:
AspectValues.dataTypeId - Controls is from/tofields are generated.
AspectValues.fromToFlag - Controls if a to HTML text entry field is generated.
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: 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.
- app/js/cascadingSelect.js
- app/model/AspectRefSet.php
- app/model/AspectValuesSet.php
- lib/smarty/plugins/function.html_aspect_select.php
Status:
- 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';
Input field capture and validation is not performed at all. Meaning you lose the from/to value when you press Next.
Suggested Future testing:
- Check three or four level classifications
- 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:
- 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
- For each aspect there is a separate generated file which initializes a set of menus that defines the
entire classification tree for that aspect.
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.
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, ..)
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
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.
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
MenuItem - Implements a single menu item
Menu - Implements a set of MenuItems
Select - Implements an HTML select, includes a set of Menus (in the items variable)
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.