/* Hey Emacs, this is -*- LPC -*- ! */ /* * $Id: error_tracker_master.c,v 1.27 2003/07/15 12:16:39 taffyd Exp $ */ /** * The nice error handler for domains to do wonderful things with. * Lots hacked by Pinkfish to make it work with the mysql handler and not * use blocking io calls. * @author Sin */ #include <board.h> #include <db.h> #include <config.h> #include <project_management.h> #define HELPER "/obj/handlers/finderror_helper" inherit "/global/path"; private void finished_count_errors( object player, int status, mixed data ); private string domain = "unset"; private string _board; private string tracker_name = "generic error tracker"; private string *other_dirs = ({ }); private int period = 604800; private int nexttime; private nosave mapping _messages; private nosave string *_queue; private nosave object _tester; private nosave string _save_file; private nosave int starttime; #define ERROR_INDEX 0 #define TYPO_INDEX 1 #define IDEA_INDEX 2 /* Define this to use the old ERROR_REPORTS file system */ #undef OLD_SYTEM void do_the_work(); /** @ignore yes */ protected void save_me() { if (domain != "unset" && _save_file) { unguarded( (: save_object, _save_file, 0 :) ); } } void create() { int retval; _messages = ([ ]); _tester = 0; seteuid(master()->creator_file(file_name())); _save_file = file_name(); if (catch(retval = unguarded((: restore_object, _save_file, 1 :))) || !retval) { string temp; // #ifdef BROKEN temp = "/save/" + replace(file_name()[1..], "/", "_"); if (catch(retval = unguarded((: restore_object, temp, 1 :))) || !retval) { /* The save file doesn't exist. Try to make it here, then * /save */ if (catch(save_me())) { _save_file = temp; } } else { _save_file = temp; } // #endif } if (!retval) { nexttime = 0; } if (nexttime < time()) { do_the_work(); } else { call_out("do_the_work", nexttime - time()); } } /** * This method sets the board to post the messages to. The board defaults * to the domain name, so this is not required for most domains. * @param board the board to post to */ void set_board(string board) { _board = board; } /* set_board() */ /** * This method returns the board to post the messages to. * @return the board to post the messages to */ string query_board() { return _board; } /* query_board() */ /** @ignore yes */ void dest_me() { save_me(); destruct(this_object()); } /** @ignore yes */ nomask mixed dwep() { destruct(this_object()); return "Destructed With Extreme Prejudice"; } /** * This method posts the results to the correct board. * @param message the message to post * @param title the title of the message */ protected varargs int post( string message, string title ) { string temp; if( undefinedp( title ) ) { temp = "Status summary for "+ capitalize( domain ) + "."; } else { temp = title; } if (_tester) { _tester->more_string(message); _tester = 0; return 0; } if (_board) { return BOARD_HAND->add_message(_board, capitalize(tracker_name), temp , message); } else { return BOARD_HAND->add_message(domain, capitalize(tracker_name), temp , message); } } string * query_classifications() { return ({ "live", "playtesting", "development" }); } /* query_classifications() */ /** * This method makes the message to post to the board from all the * other rubbish. * @return the message to post */ string query_post_message() { string message; int diff; if (!sizeof(_messages)) { message = "Congratulations! There are no errors in all of " + capitalize(domain) + "! Excellent work, everyone.\n"; } else { message = ""; foreach( string key in query_classifications() ) { if ( _messages[ key ] ) { message += "There are reports in the following " + key + " areas:\n\n" + _messages[ key ] + "\n"; } else { if ( key == "live" ) { message += "\n\nThere are no live bugs in all of " + capitalize(domain) + ". Good work, everyone!\n"; } else { message += "\n\nThere are no outstanding " + key + " bugs.\n"; } } } } diff = (real_time() - starttime); message += "\n\nThis message took " + diff + " second" + (diff != 1 ? "s" : "") + " to produce.\n"; return message; } /* query_post_message() */ string query_project_message() { string str; class project* projects, *my_projects; class project project; mapping status; str = "\nCurrent Domain Projects:\n\n"; projects = PROJECT_HANDLER->filter_by_query(([ "domains" : domain ])); if (sizeof(projects)) { status = unique_mapping( projects, (: STATUS[((class project)$1)->status] :) ); map_delete( status, "play" ); if ( !sizeof(status) ){ str += "This domain is project free.\n"; } foreach( string key in sort_array( keys( status ), 1 ) ) { str += "Projects classified as " + upper_case(key) + ":\n"; my_projects = sort_array(status[key], (: strcmp(((class project)$1)->name, ((class project)$2)->name) :) ); foreach (project in my_projects) { str += " * " + project->name + " - " + query_multiple_short(sort_array(map(project->creators, (: capitalize :) ), 1)) + "\n"; } str += "\n"; } } else { str += "This domain is project free.\n"; } return str; } /** * This method goes over all the directories and makes up the * complete report. */ protected void iterate() { #ifdef OLD string message; if (sizeof(_queue)) { string dir = _queue[0]; if (sizeof(_queue) > 1) { _queue = _queue[1..]; } else { _queue = ({ }); } if (strsrch (dir, "_dev") != -1) { dodir(dir, (: _dev_msg += ({ $1 }), iterate() :)); } else if (strsrch (dir, "_pt") != -1) { dodir(dir, (: _pt_msg += ({ $1 }), iterate() :)); } else { dodir(dir, (: _messages += ({ $1 }), iterate() :)); } } else { message = query_project_message() + "\n"; message += query_post_message(); post(message); _messages = ({ }); //This check is used by the AM domain error tracker. if( function_exists( "domain_customisation" , this_object() ) ) { call_out( "domain_customisation" , 1 ); } } #endif HELPER->query_errors_in_dirs(_queue, (: finished_count_errors :) ); } /* iterate() */ /** * This method initialises all the variables for a error run. */ void initialise_variables() { _queue = ({ "/d/" + domain + "%" }) + other_dirs; _messages = ([ ]); } /* initialise_variables() */ /** * This bit does the actual processing and prints the results. */ void do_the_work() { if (domain == "unset") return; nexttime = time() + period; remove_call_out("do_the_work"); call_out("do_the_work", period); save_me(); starttime = real_time(); initialise_variables(); iterate(); } /** @ignore yes */ varargs void test() { _tester = this_player(); starttime = real_time(); initialise_variables(); iterate(); } /** * This method sets the name of the tracker. * @param name the name of the tracker */ void set_name(string name) { tracker_name = name; save_me(); } /** * This method sets the domain of the tracker. * @param name the domain of the tracker */ void set_domain(string name) { domain = name; save_me(); } /** * This method returns the domain of the tracker. * @return the domain of the tracker */ string query_domain() { return domain; } /* query_domain() */ /** * This method sets how long between each time the error tracker fires. * @param length the length of time in seconds */ void set_period(int length) { remove_call_out("do_the_work"); nexttime -= period; period = length; nexttime += period; save_me(); /* if (nexttime < time()) { do_the_work(); } else { call_out("do_the_work", nexttime - time()); } */ } /** * This method sets the list of other directories to use for tracking * errors. * @param others the other directories to use */ void set_other_dirs(string *others) { if (arrayp(others)) { other_dirs = others; } else { other_dirs = ({ }); } save_me(); } /** @ignore yes */ mixed stats() { return ({ ({ "domain", domain }), ({ "name", tracker_name }), ({ "period", period }), ({ "next time", ctime(nexttime) }), ({ "testing by", _tester }), ({ "queue length", sizeof(_queue) }), ({ "message length", sizeof(_messages) }), ({ "other dirs", sizeof(other_dirs) ? implode(other_dirs, ", ") : 0 }), ({ "save file", _save_file }), }); } protected string classify_directory(string path) { if ( strsrch( path, "_pt" ) > -1 ) { return "playtesting"; } if ( strsrch( path, "_dev" ) > -1 ) { return "development"; } return "live"; } /* classify_directory() */ private int sort_errors( mapping mdata, string key1, string key2 ) { int total1, total2; total1 = mdata[key1][0] + mdata[key1][1] + mdata[key1][2]; total2 = mdata[key2][0] + mdata[key2][1] + mdata[key2][2]; if ( total1 > total2 ) { return -1; } else { if ( total1 == total2 ) { return 0; } else { return 1; } } } /* sort_errors() */ private void finished_count_errors( object player, int status, mixed data ) { int *count; string *paths; mapping errors = ([ ]); // int *total = ({ 0, 0, 0 }); mapping total = ([ ]); string txt; string classification; mapping mdata; string message; // tell_creator( player, "%O, %O\n", status, data ); foreach( mapping direc in data ) { classification = classify_directory( direc[ "Directory" ] ); if ( !errors[ classification ] ) { errors[ classification ] = ([ ]); } if ( !total[ classification ] ) { total[ classification ] = ({ 0, 0, 0 }); } count = errors[ classification ][ direc[ "Directory" ] ]; if ( undefinedp( count ) ) { count = ({ 0, 0, 0 }); } switch( direc[ "Type" ] ) { case "BUG": count[ 0 ] = direc[ "C" ]; total[ classification ][ 0 ] += count[ 0 ]; break; case "TYPO": count[ 1 ] = direc[ "C" ]; total[ classification ][ 1 ] += count[ 1 ]; break; case "IDEA": default: count[ 2 ] = direc[ "C" ]; total[ classification ][ 2 ] += count[ 2 ]; } errors[ classification ][ direc[ "Directory" ] ] = count; } foreach ( classification, mdata in errors ) { // paths = sort_array( keys( mdata ), 1 ); paths = sort_array( keys( mdata ), (: sort_errors( $(mdata), $1, $2 ) :) ); txt = "BUGS TYPO IDEA [Assigned] Dir\n"; foreach( string key in paths ) { string* assigned; assigned = master()->query_assigned_to_directory(key); if (!sizeof(assigned)) { assigned = ({ "No one" }); } txt += sprintf( "%4d %4d %4d %-11s %s\n", mdata[ key ][ 0 ], mdata[ key ][ 1 ], mdata[ key ][ 2 ], query_multiple_short(assigned), key ); } txt += "-------------\n"; txt += sprintf( "%4d %4d %4d\n", total[ classification ][ 0 ], total[ classification ][ 1 ], total[ classification ][ 2 ] ); _messages[ classification ] = txt; } message = query_project_message() + "\n"; message += query_post_message(); if (function_exists("extend_error_message", this_object())) { message += this_object()->extend_error_message( copy( errors ) ); } post(message); _messages = ([ ]); //This check is used by the AM domain error tracker. if( function_exists( "domain_customisation" , this_object() ) ) { call_out( "domain_customisation" , 1 ); } } /* finished_count_errors() */