source: gsdl/trunk/src/recpt/dynamicclassifieraction.cpp@ 16059

Last change on this file since 16059 was 16059, checked in by mdewsnip, 16 years ago

(Adding dynamic classifiers) Removed an unused variable.

  • Property svn:executable set to *
File size: 21.5 KB
Line 
1/**********************************************************************
2 *
3 * dynamicclassifieraction.cpp --
4 * Copyright (C) 2008 DL Consulting Ltd
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 *********************************************************************/
25
26#include "dynamicclassifieraction.h"
27#include "recptprototools.h"
28
29
30dynamicclassifieraction::dynamicclassifieraction ()
31{
32 recpt = NULL;
33
34 cgiarginfo arg_ainfo;
35 arg_ainfo.shortname = "dcl";
36 arg_ainfo.longname = "dynamic classifier ID";
37 arg_ainfo.multiplechar = true;
38 arg_ainfo.defaultstatus = cgiarginfo::weak;
39 arg_ainfo.argdefault = "";
40 arg_ainfo.savedarginfo = cgiarginfo::must;
41 argsinfo.addarginfo (NULL, arg_ainfo);
42
43 arg_ainfo.shortname = "dcn";
44 arg_ainfo.longname = "dynamic classifier node";
45 arg_ainfo.multiplechar = true;
46 arg_ainfo.defaultstatus = cgiarginfo::weak;
47 arg_ainfo.argdefault = "";
48 arg_ainfo.savedarginfo = cgiarginfo::must;
49 argsinfo.addarginfo (NULL, arg_ainfo);
50}
51
52
53dynamicclassifieraction::~dynamicclassifieraction()
54{
55}
56
57
58bool dynamicclassifieraction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args,
59 recptprotolistclass *protos, ostream &logout)
60{
61 return true;
62}
63
64
65void dynamicclassifieraction::get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos,
66 response_t &response,text_t &response_data,
67 ostream &logout)
68{
69 response = content;
70 response_data = "text/html";
71}
72
73
74// define all the macros which might be used by other actions to produce pages.
75void dynamicclassifieraction::define_external_macros (displayclass &disp, cgiargsclass &args,
76 recptprotolistclass *protos, ostream &logout)
77{
78 // A valid collection server is vital
79 recptproto *collectproto = protos->getrecptproto (args["c"], logout);
80 if (collectproto == NULL)
81 {
82 logout << "dynamicclassifieraction::define_external_macros called with NULL collectproto\n";
83 return;
84 }
85
86 // Define _dynamicclassifiernavbarentries_ to add buttons to the navigation bar for the dynamic classifiers
87 text_t navigation_bar_entries = "";
88 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, args["c"], logout);
89 text_tmap::iterator dynamic_classifier_iterator = cinfo->dynamic_classifiers.begin();
90 while (dynamic_classifier_iterator != cinfo->dynamic_classifiers.end())
91 {
92 text_t dynamic_classifier_id = (*dynamic_classifier_iterator).first;
93 navigation_bar_entries += "_navbarspacer_";
94 navigation_bar_entries += "_navtab_(_gwcgi_?c=" + args["c"] + "&amp;a=dc&amp;dcl=" + dynamic_classifier_id + "," + dynamic_classifier_id;
95 if (args["a"] == "dc" && args["dcl"] == dynamic_classifier_id)
96 {
97 navigation_bar_entries += ",selected";
98 }
99 navigation_bar_entries += ")";
100 dynamic_classifier_iterator++;
101 }
102
103 disp.setmacro("dynamicclassifiernavbarentries", displayclass::defaultpackage, navigation_bar_entries);
104}
105
106
107// define all the macros which are related to pages generated
108// by this action. we also load up the formatinfo structure
109// here (it's used in do_action as well as here)
110void dynamicclassifieraction::define_internal_macros (displayclass &disp, cgiargsclass &args,
111 recptprotolistclass *protos, ostream &logout)
112{
113 // define_internal_macros sets the following macros:
114}
115
116
117bool dynamicclassifieraction::do_action(cgiargsclass &args, recptprotolistclass *protos,
118 browsermapclass *browsers, displayclass &disp,
119 outconvertclass &outconvert, ostream &textout,
120 ostream &logout)
121{
122 // A valid collection server is vital
123 recptproto *collectproto = protos->getrecptproto (args["c"], logout);
124 if (collectproto == NULL)
125 {
126 logout << "dynamicclassifieraction::do_action called with NULL collectproto\n";
127 return false;
128 }
129
130 textout << outconvert << disp << "_dynamicclassifier:header_\n";
131 textout << outconvert << disp << "_dynamicclassifier:content_\n";
132
133 // Check a dynamic classifier ID has been specified
134 text_t arg_dcl = args["dcl"];
135 if (arg_dcl.empty())
136 {
137 textout << outconvert << disp << "Error: Missing dcl argument.\n";
138 textout << outconvert << disp << "_dynamicclassifier:footer_\n";
139 return true;
140 }
141
142 // Check the dynamic classifier ID is valid (ie. there is an entry in the collect.cfg file for it)
143 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, args["c"], logout);
144 if (cinfo->dynamic_classifiers.find(arg_dcl) == cinfo->dynamic_classifiers.end())
145 {
146 textout << outconvert << disp << "Error: Invalid dcl value \"" << arg_dcl << "\".\n";
147 textout << outconvert << disp << "_dynamicclassifier:footer_\n";
148 return true;
149 }
150
151 // Parse the classifier options from the specification
152 text_t classifier_specification = cinfo->dynamic_classifiers[arg_dcl];
153 text_tmap classifier_options = parse_classifier_options (classifier_specification, args);
154
155 // Output the "<ID>Header" format statement if there is one
156 text_t classifier_header_format_statement = "";
157 get_formatstring (arg_dcl + "Header", cinfo->format, classifier_header_format_statement);
158 textout << outconvert << disp << classifier_header_format_statement << "\n";
159
160 // Output the dynamic classifier, beginning with the (optional) grouping nodes
161 text_t selected_grouping_node_OID = "";
162 if (!classifier_options["-group_using"].empty())
163 {
164 selected_grouping_node_OID = output_grouping_nodes (classifier_options, args, collectproto, browsers, disp, outconvert, textout, logout);
165 }
166
167 // Prepare to output the classifier (and maybe document) nodes
168 text_t classifier_node_OID = args["dcn"];
169 int classifier_node_indent = 0;
170
171 // Simple case at the top level: just output the child classifier nodes
172 if (args["dcn"] == selected_grouping_node_OID)
173 {
174 text_t metadata_value_filter = args["dcn"] + "*";
175 output_child_classifier_nodes (classifier_node_OID, metadata_value_filter, classifier_node_indent, classifier_options, args, collectproto, browsers, disp, outconvert, textout, logout);
176 }
177
178 // More complex case below the top level
179 else
180 {
181 // Output the parent classifier nodes
182 output_parent_classifier_nodes (classifier_node_OID, classifier_node_indent, classifier_options, args, collectproto, browsers, disp, outconvert, textout, logout);
183
184 // Display the selected classifier node
185 text_tarray classifier_node_labels;
186 splitchar(classifier_node_OID.begin(), classifier_node_OID.end(), '|', classifier_node_labels);
187 text_t classifier_node_label = classifier_node_labels.back();
188 text_t classifier_node_numleafdocs = "?";
189 display_classifier_node (classifier_node_OID, classifier_node_label, classifier_node_numleafdocs, classifier_node_indent, args, collectproto, browsers, disp, outconvert, textout, logout);
190 classifier_node_indent++;
191
192 // Output the child classifier nodes
193 text_t metadata_value_filter = args["dcn"] + "|*";
194 output_child_classifier_nodes (classifier_node_OID, metadata_value_filter, classifier_node_indent, classifier_options, args, collectproto, browsers, disp, outconvert, textout, logout);
195
196 // Get the document nodes at this level
197 text_t metadata_element_name = classifier_options["metadata_element_name"];
198 text_t classifier_node_metadata_value = args["dcn"];
199 text_t sort_documents_by = classifier_options["-sort_documents_by"];
200 FilterResponse_t documents_response;
201 get_documents_with_metadata_value (metadata_element_name, classifier_node_metadata_value, sort_documents_by, args["c"], collectproto, documents_response, logout);
202
203 // Display the document nodes
204 display_document_nodes (documents_response, classifier_node_indent, args, collectproto, browsers, disp, outconvert, textout, logout);
205 }
206
207 // Output the "<ID>Footer" format statement if there is one
208 text_t classifier_footer_format_statement = "";
209 get_formatstring (arg_dcl + "Footer", cinfo->format, classifier_footer_format_statement);
210 textout << outconvert << disp << classifier_footer_format_statement << "\n";
211
212 textout << outconvert << disp << "_dynamicclassifier:footer_\n";
213 return true;
214}
215
216
217text_tmap dynamicclassifieraction::parse_classifier_options (text_t classifier_specification, cgiargsclass &args)
218{
219 text_tmap classifier_options;
220
221 // Split the classifier specification string by spaces
222 text_tlist classifier_specification_parts;
223 splitchar (classifier_specification.begin(), classifier_specification.end(), ' ', classifier_specification_parts);
224
225 // The metadata element to classify by should be the first value
226 classifier_options["metadata_element_name"] = classifier_specification_parts.front();
227 classifier_specification_parts.pop_front();
228
229 // Parse options from the remainder of the classifier specification
230 while (!classifier_specification_parts.empty())
231 {
232 // Parse the option name
233 text_t classifier_option_name = classifier_specification_parts.front();
234 classifier_specification_parts.pop_front();
235
236 // Check if the option has a value (it may just be a flag, in which case we use "1" as the value)
237 text_t classifier_option_value = "1";
238 if (!classifier_specification_parts.empty() && !starts_with(classifier_specification_parts.front(), "-"))
239 {
240 classifier_option_value = classifier_specification_parts.front();
241 classifier_specification_parts.pop_front();
242 }
243
244 // Record the option
245 classifier_options[classifier_option_name] = classifier_option_value;
246 }
247
248 return classifier_options;
249}
250
251
252text_t dynamicclassifieraction::output_grouping_nodes (text_tmap classifier_options, cgiargsclass &args,
253 recptproto *collectproto, browsermapclass *browsers,
254 displayclass &disp, outconvertclass &outconvert,
255 ostream &textout, ostream &logout)
256{
257 // Get all the metadata values for the specified element, and group them according to the "-group_using" value
258 text_t metadata_element_name = classifier_options["metadata_element_name"];
259 text_t metadata_value_grouping_expression = classifier_options["-group_using"];
260 FilterResponse_t grouping_nodes_response;
261 bool request_success = get_metadata_values (metadata_element_name, "", metadata_value_grouping_expression, args["c"], collectproto, grouping_nodes_response, logout);
262
263 // If the request failed then it's probably because the collection isn't using an SQL infodbtype
264 if (request_success == false)
265 {
266 textout << outconvert << disp << "Error: Dynamic classifier functionality is not available. Please check you are using an SQL infodbtype and the collection has been rebuilt.\n";
267 return "";
268 }
269
270 // Check some grouping nodes were returned
271 if (grouping_nodes_response.docInfo.empty())
272 {
273 return "";
274 }
275
276 // If no classifier node has been specified automatically go to the first grouping node
277 if (args["dcn"] == "")
278 {
279 args["dcn"] = grouping_nodes_response.docInfo.front().OID;
280 }
281
282 text_t selected_grouping_node_OID = "";
283 ResultDocInfo_tarray::iterator grouping_node_iterator = grouping_nodes_response.docInfo.begin();
284 while (grouping_node_iterator != grouping_nodes_response.docInfo.end())
285 {
286 // Is this the grouping node that is currently selected?
287 if ((*grouping_node_iterator).OID == args["dcn"])
288 {
289 selected_grouping_node_OID = (*grouping_node_iterator).OID;
290 }
291
292 // Add the necessary metadata required to display the grouping nodes correctly
293 (*grouping_node_iterator).metadata["doctype"].values.push_back ("classify");
294 (*grouping_node_iterator).metadata["haschildren"].values.push_back ("1");
295 (*grouping_node_iterator).metadata["numleafdocs"].values.push_back ("?"); // We can't determine this without more database requests
296 (*grouping_node_iterator).metadata["Title"].values.push_back ((*grouping_node_iterator).OID);
297 grouping_node_iterator++;
298 }
299
300 // Get the format statement for this classifier if there is one, or use the browser's default otherwise
301 text_t formatstring;
302 text_t classifier_type = "HList";
303 browserclass *bptr = browsers->getbrowser (classifier_type);
304 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, args["c"], logout);
305 if (!get_formatstring (args["dcl"], classifier_type, cinfo->format, formatstring))
306 {
307 formatstring = bptr->get_default_formatstring();
308 }
309 format_t *formatlistptr = new format_t();
310 text_tset metadata;
311 bool getParents = false;
312 parse_formatstring (formatstring, formatlistptr, metadata, getParents);
313 bool use_table = is_table_content (formatlistptr);
314
315 // Display the grouping nodes
316 bptr->output_section_group (grouping_nodes_response, args, args["c"], 0, formatlistptr, use_table, metadata, getParents, collectproto, disp, outconvert, textout, logout);
317
318 return selected_grouping_node_OID;
319}
320
321
322void dynamicclassifieraction::output_parent_classifier_nodes (text_t classifier_node_OID,
323 int& classifier_node_indent,
324 text_tmap classifier_options, cgiargsclass &args,
325 recptproto *collectproto, browsermapclass *browsers,
326 displayclass &disp, outconvertclass &outconvert,
327 ostream &textout, ostream &logout)
328{
329 // Determine the parent classifier nodes
330 text_tarray parent_classifier_node_labels;
331 splitchar(classifier_node_OID.begin(), classifier_node_OID.end(), '|', parent_classifier_node_labels);
332 parent_classifier_node_labels.pop_back();
333
334 // Display the parent classifier nodes
335 text_t parent_classifier_node_OID = "";
336 text_tarray::iterator parent_classifier_node_labels_iterator = parent_classifier_node_labels.begin();
337 while (parent_classifier_node_labels_iterator != parent_classifier_node_labels.end())
338 {
339 parent_classifier_node_OID += (parent_classifier_node_OID != "" ? "|" : "");
340 parent_classifier_node_OID += *parent_classifier_node_labels_iterator;
341 text_t parent_classifier_node_label = *parent_classifier_node_labels_iterator;
342 text_t parent_classifier_node_numleafdocs = "?"; // We can't determine this without more database requests
343 display_classifier_node (parent_classifier_node_OID, parent_classifier_node_label, parent_classifier_node_numleafdocs, classifier_node_indent, args, collectproto, browsers, disp, outconvert, textout, logout);
344 classifier_node_indent++;
345
346 parent_classifier_node_labels_iterator++;
347 }
348}
349
350
351void dynamicclassifieraction::output_child_classifier_nodes (text_t classifier_node_OID,
352 text_t metadata_value_filter,
353 int& classifier_node_indent,
354 text_tmap classifier_options, cgiargsclass &args,
355 recptproto *collectproto, browsermapclass *browsers,
356 displayclass &disp, outconvertclass &outconvert,
357 ostream &textout, ostream &logout)
358{
359 // Get all the metadata values for the specified element that match the filter
360 text_t metadata_element_name = classifier_options["metadata_element_name"];
361 FilterResponse_t metadata_values_response;
362 bool request_success = get_metadata_values (metadata_element_name, metadata_value_filter, "", args["c"], collectproto, metadata_values_response, logout);
363
364 // If the request failed then it's probably because the collection isn't using an SQL infodbtype
365 if (request_success == false)
366 {
367 textout << outconvert << disp << "Error: Dynamic classifier functionality is not available. Please check you are using an SQL infodbtype and the collection has been rebuilt.\n";
368 return;
369 }
370
371 // After processing any hierarchical metadata values we're left with the child classifer nodes
372 map<text_t, int, lttext_t> child_classifier_nodes;
373 text_t current_position = args["dcn"];
374 ResultDocInfo_tarray::iterator metadata_value_iterator = metadata_values_response.docInfo.begin();
375 while (metadata_value_iterator != metadata_values_response.docInfo.end())
376 {
377 text_t metadata_value = (*metadata_value_iterator).OID;
378
379 // If we're not at the top-level we need to remove the current position from the metadata values
380 if (current_position != "" && starts_with(metadata_value, current_position + "|"))
381 {
382 metadata_value = substr(metadata_value.begin() + (current_position + "|").size(), metadata_value.end());
383 }
384
385 // Is this metadata value hierarchical?
386 text_t::iterator hierarchy_split_position = findchar(metadata_value.begin(), metadata_value.end(), '|');
387 if (hierarchy_split_position != metadata_value.end())
388 {
389 // Yes, so use the first part of the hierarchy only
390 metadata_value = substr(metadata_value.begin(), hierarchy_split_position);
391 }
392
393 // Create a node for this metadata value if we haven't seen it before
394 if (child_classifier_nodes.find(metadata_value) == child_classifier_nodes.end())
395 {
396 child_classifier_nodes[metadata_value] = 0;
397 }
398
399 // Increment the occurrence count
400 child_classifier_nodes[metadata_value] += (*metadata_value_iterator).result_num;
401
402 metadata_value_iterator++;
403 }
404
405 // Display the child classifier nodes
406 map<text_t, int, lttext_t>::iterator child_classifier_nodes_iterator = child_classifier_nodes.begin();
407 while (child_classifier_nodes_iterator != child_classifier_nodes.end())
408 {
409 text_t child_classifier_node_OID = (*child_classifier_nodes_iterator).first;
410 if (classifier_node_OID != "")
411 {
412 child_classifier_node_OID = classifier_node_OID + "|" + child_classifier_node_OID;
413 }
414 text_t child_classifier_node_label = (*child_classifier_nodes_iterator).first;
415 int child_classifier_node_numleafdocs = (*child_classifier_nodes_iterator).second;
416 display_classifier_node (child_classifier_node_OID, child_classifier_node_label, child_classifier_node_numleafdocs, classifier_node_indent, args, collectproto, browsers, disp, outconvert, textout, logout);
417 child_classifier_nodes_iterator++;
418 }
419}
420
421
422void dynamicclassifieraction::display_classifier_node (text_t classifier_node_OID, text_t classifier_node_label,
423 text_t classifier_node_numleafdocs, int classifier_node_indent,
424 cgiargsclass &args, recptproto *collectproto,
425 browsermapclass *browsers, displayclass &disp,
426 outconvertclass &outconvert, ostream &textout,
427 ostream &logout)
428{
429 // Generate the ResultDocInfo_t containing the information for the classifier node
430 ResultDocInfo_t classifier_node;
431 classifier_node.OID = classifier_node_OID;
432 classifier_node.metadata["doctype"].values.push_back ("classify");
433 classifier_node.metadata["haschildren"].values.push_back ("1");
434 classifier_node.metadata["numleafdocs"].values.push_back (classifier_node_numleafdocs);
435 classifier_node.metadata["Title"].values.push_back (classifier_node_label);
436
437 // Get the format statement for this classifier if there is one, or use the browser's default otherwise
438 text_t formatstring;
439 text_t classifier_type = "VList";
440 browserclass *bptr = browsers->getbrowser (classifier_type);
441 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, args["c"], logout);
442 if (!get_formatstring (args["dcl"], classifier_type, cinfo->format, formatstring))
443 {
444 formatstring = bptr->get_default_formatstring();
445 }
446 format_t *formatlistptr = new format_t();
447 text_tset metadata;
448 bool getParents = false;
449 parse_formatstring (formatstring, formatlistptr, metadata, getParents);
450 bool use_table = is_table_content (formatlistptr);
451
452 // Display the classifier node
453 bptr->output_section_group (classifier_node, args, args["c"], classifier_node_indent, formatlistptr, use_table, metadata, getParents, collectproto, disp, outconvert, textout, logout);
454}
455
456
457void dynamicclassifieraction::display_document_nodes (FilterResponse_t documents_response, int document_nodes_indent,
458 cgiargsclass &args, recptproto *collectproto,
459 browsermapclass *browsers, displayclass &disp,
460 outconvertclass &outconvert, ostream &textout,
461 ostream &logout)
462{
463 // Get the format statement for this classifier if there is one, or use the browser's default otherwise
464 text_t formatstring;
465 text_t classifier_type = "VList";
466 browserclass *bptr = browsers->getbrowser (classifier_type);
467 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, args["c"], logout);
468 if (!get_formatstring (args["dcl"], classifier_type, cinfo->format, formatstring))
469 {
470 formatstring = bptr->get_default_formatstring();
471 }
472 format_t *formatlistptr = new format_t();
473 text_tset metadata;
474 bool getParents = false;
475 parse_formatstring (formatstring, formatlistptr, metadata, getParents);
476 bool use_table = is_table_content (formatlistptr);
477
478 // Request the necessary metadata for displaying the documents
479 text_tarray document_OIDs;
480 ResultDocInfo_tarray::iterator document_iterator = documents_response.docInfo.begin();
481 while (document_iterator != documents_response.docInfo.end())
482 {
483 document_OIDs.push_back ((*document_iterator).OID);
484 document_iterator++;
485 }
486 FilterResponse_t document_nodes_response;
487 get_info (document_OIDs, args["c"], args["l"], metadata, getParents, collectproto, document_nodes_response, logout);
488
489 // Display the document nodes
490 bptr->output_section_group (document_nodes_response, args, args["c"], document_nodes_indent, formatlistptr, use_table, metadata, getParents, collectproto, disp, outconvert, textout, logout);
491}
Note: See TracBrowser for help on using the repository browser.