Пример #1
0
// This function runs the sprint burndown history logger for $bible.
// If no $bible is passed, it will do all Bibles.
// If $mail is true, it will mail the burndown chart to the subscribed users.
// If $mail is false, it decides on its own whether to mail the chart to the users.
void sprint_burndown (string bible, bool email)
{
  int localseconds = filter_date_local_seconds (filter_date_seconds_since_epoch ());
  int year = filter_date_numerical_year (localseconds);
  int month = filter_date_numerical_month (localseconds);
  int monthday = filter_date_numerical_month_day (localseconds); // 1 to 31.
  int weekday = filter_date_numerical_week_day (localseconds); // 0 (for Sunday) through 6 (for Saturday).
  int hour = filter_date_numerical_hour (localseconds);
  bool sprintstart = false;
  bool sprintfinish = false;

  // Every Friday at 2 PM (14:00h) it sends email about the sprint progress.
  if ((weekday == 5) && (hour == 14)) email = true;
  // On the first business day of the month, at 10 AM, send email about the start of the sprint.
  if (filter_date_is_first_business_day_of_month (monthday, weekday) && (hour == 10)) {
    email = true;
    sprintstart = true;
  }
  // On the last business day of the month, at 2 PM (14:00h), send email about the end of the sprint.
  if ((monthday == filter_date_get_last_business_day_of_month (year, month)) && (hour == 14)) {
    email = true;
    sprintfinish = true;
  }
  
  
  // Determine what to do, or to quit.
  if (!email && !sprintstart && !sprintfinish) {
    if (hour != 1) return;
  }
  

  Database_Logs::log ("Updating Sprint information", Filter_Roles::manager ());

  Webserver_Request request;
  Database_Mail database_mail = Database_Mail (&request);
  Database_Sprint database_sprint = Database_Sprint ();

  
  // Determine year / month / day of the current sprint.
  // If this script runs from midnight till early morning,
  // it applies to the day before.
  // If the script runs during the day, it applies to today.
  if (hour <= 6) {
    localseconds -= (3600 * 6);
  }
  year = filter_date_numerical_year (localseconds);
  month = filter_date_numerical_month (localseconds);
  monthday = filter_date_numerical_month_day (localseconds); // 1 to 31.
  
  
  vector <string> bibles = {bible};
  if (bible == "") {
    bibles = request.database_bibles()->getBibles ();
  }
  

  for (auto bible : bibles) {

    
    // Get the total number of tasks for this sprint,
    // and the average percentage of completion of them,
    // and store this information in the sprint history table.
    vector <int> ids = database_sprint.getTasks (bible, year, month);
    vector <int> percentages;
    for (auto id : ids) {
      percentages.push_back (database_sprint.getComplete (id));
    }
    int tasks = ids.size ();
    int complete = 0;
    if (tasks != 0) {
      for (auto percentage : percentages) complete += percentage;
      complete = round ((float) complete / (float) tasks);
    }
    database_sprint.logHistory (bible, year, month, monthday, tasks, complete);
    
    
    // Send email if requested.
    if (email) {
      if (tasks) {
        // Only mail if the current sprint contains tasks.
        string scategories = Database_Config_Bible::getSprintTaskCompletionCategories (bible);
        vector <string> categories = filter_string_explode (scategories, '\n');
        int category_count = categories.size();
        int category_percentage = round (100 / category_count);
        vector <string> users = request.database_users ()->getUsers ();
        for (auto user : users) {
          if (request.database_config_user()->getUserSprintProgressNotification (user)) {
            
            string subject = translate("Team's progress in Sprint");
            if (sprintstart) subject = translate("Sprint has started");
            if (sprintfinish) subject = translate("Sprint has finished");
            subject +=  " | " + bible;
            
            vector <string> body;
            
            body.push_back ("<h3>" + translate("Sprint Planning and Team's Progress") + " | " + bible + "</h3>");
            body.push_back ("<table class='burndown'>");
            vector <int> tasks = database_sprint.getTasks (bible, year, month);
            for (auto id : tasks) {
              body.push_back ("<tr>");
              string title = database_sprint.getTitle (id);
              body.push_back ("<td>" + title + "</td>");
              int complete = database_sprint.getComplete (id);
              string text;
              for (int i = 0; i < round (complete / category_percentage); i++) text.append ("▓");
              for (int i = 0; i < category_count - round (complete / category_percentage); i++) text.append ("▁");
              body.push_back ("<td>" + text + "</td>");
              body.push_back ("</tr>");
            }
            body.push_back ("</table>");
            
            body.push_back ("<h3>" + translate("Sprint Burndown Chart - Remaining Tasks") + "</h3>");
            string burndownchart = sprint_create_burndown_chart (bible, year, month);
            body.push_back ("<p>" + burndownchart + "</p>");
            
            if (!body.empty ()) {
              string mailbody = filter_string_implode (body, "\n");
              if (!client_logic_client_enabled ()) database_mail.send (user, subject, mailbody);
            }
            
          }
        }
      } else {
        
        // Since there are no tasks, no mail will be sent: Make a logbook entry.
        Database_Logs::log ("No tasks in this Sprint: No email was sent");
      }
    }
  }
}
Пример #2
0
string sprint_index (void * webserver_request)
{
  Webserver_Request * request = (Webserver_Request *) webserver_request;
  Database_Sprint database_sprint;

  
  string page;
  Assets_Header header = Assets_Header (translate("Sprint"), request);
  header.addBreadCrumb (menu_logic_tools_menu (), menu_logic_tools_text ());
  page = header.run ();
  Assets_View view;
  
  
  if (request->query.count ("previoussprint")) {
    int month = request->database_config_user()->getSprintMonth ();
    int year = request->database_config_user()->getSprintYear ();
    filter_date_get_previous_month (month, year);
    request->database_config_user()->setSprintMonth (month);
    request->database_config_user()->setSprintYear (year);
  }
  
  
  if (request->query.count ("currentprint")) {
    request->database_config_user()->setSprintMonth (filter_date_numerical_month (filter_date_seconds_since_epoch ()));
    request->database_config_user()->setSprintYear (filter_date_numerical_year (filter_date_seconds_since_epoch ()));
  }
  
  
  if (request->query.count ("nextsprint")) {
    int month = request->database_config_user()->getSprintMonth ();
    int year = request->database_config_user()->getSprintYear ();
    filter_date_get_next_month (month, year);
    request->database_config_user()->setSprintMonth (month);
    request->database_config_user()->setSprintYear (year);
  }
  
  
  string bible = access_bible_clamp (webserver_request, request->database_config_user()->getBible ());
  int month = request->database_config_user()->getSprintMonth ();
  int year = request->database_config_user()->getSprintYear ();
  

  if (request->post.count ("id")) {
    string id = request->post ["id"];
    string checked = request->post ["checked"];
    if (id.length () >= 9) {
      // Remove "task".
      id.erase (0, 4);
      // Convert the fragment to an integer.
      int identifier = convert_to_int (id);
      // Find the fragment "box".
      size_t pos = id.find ("box");
      if (pos != string::npos) {
        // Remove the fragment "box".
        id.erase (0, pos + 3);
        // Convert the box to an integer.
        int box = convert_to_int (id);
        string categorytext = Database_Config_Bible::getSprintTaskCompletionCategories (bible);
        vector <string> categories = filter_string_explode (categorytext, '\n');
        int category_count = categories.size ();
        float category_percentage = 100 / category_count;
        int percentage;
        bool on = (checked == "true");
        if (on) percentage = round ((box + 1) * category_percentage);
        else percentage = round (box * category_percentage);
        database_sprint.updateComplete (identifier, percentage);
      }
    }
    return "";
  }
  
  
  if (request->post.count ("add")) {
    string title = request->post ["add"];
    database_sprint.storeTask (bible, year, month, title);
    view.set_variable ("success", translate("New task added"));
    // Focus the entry for adding tasks only in case a new task was added.
    view.set_variable ("autofocus", "autofocus");
  }
  
  
  if (request->query.count ("mail")) {
    string mail = request->query ["mail"];
    sprint_burndown (bible, true);
    view.set_variable ("success", translate("The information was mailed to the subscribers"));
  }
  
  
  if (request->query.count ("bible")) {
    bible = request->query ["bible"];
    if (bible == "") {
      Dialog_List dialog_list = Dialog_List ("index", translate("Select which Bible to display the Sprint for"), "", "");
      vector <string> bibles = access_bible_bibles (request);
      for (auto & bible : bibles) {
        dialog_list.add_row (bible, "bible", bible);
      }
      page += dialog_list.run();
      return page;
    } else {
      request->database_config_user()->setBible (bible);
    }
  }
  
  
  bible = access_bible_clamp (webserver_request, request->database_config_user()->getBible ());
  
  
  int id = convert_to_int (request->query ["id"]);
  
  
  if (request->query.count ("remove")) {
    database_sprint.deleteTask (id);
    view.set_variable ("success", translate("The task was removed"));
  }
  
  
  if (request->query.count ("moveback")) {
    filter_date_get_previous_month (month, year);
    database_sprint.updateMonthYear (id, month, year);
    view.set_variable ("success", translate("The task was moved to the previous sprint"));
    request->database_config_user()->setSprintMonth (month);
    request->database_config_user()->setSprintYear (year);
  }
                        
                        
  if (request->query.count ("moveforward")) {
    filter_date_get_next_month (month, year);
    database_sprint.updateMonthYear (id, month, year);
    view.set_variable ("success", translate("The task was moved to the next sprint"));
    request->database_config_user()->setSprintMonth (month);
    request->database_config_user()->setSprintYear (year);
  }

  
  if (request->post.count ("categories")) {
    string categories = request->post ["categories"];
    vector <string> categories2;
    categories = filter_string_trim (categories);
    vector <string> vcategories = filter_string_explode (categories, '\n');
    for (auto category : vcategories) {
      category = filter_string_trim (category);
      if (category != "") categories2.push_back (category);
    }
    categories = filter_string_implode (categories2, "\n");
    Database_Config_Bible::setSprintTaskCompletionCategories (bible, categories);
  }
  
  
  view.set_variable ("bible", bible);
  view.set_variable ("sprint", locale_logic_month (month) + " " + convert_to_string (year));

  
  string categorytext = Database_Config_Bible::getSprintTaskCompletionCategories (bible);
  view.set_variable ("categorytext", categorytext);
  vector <string> vcategories = filter_string_explode (categorytext, '\n');
  string categories;
  for (auto category : vcategories) {
    categories.append ("<td>" + category + "</td>\n");
  }
  view.set_variable ("categories", categories);
  
  
  string tasks;
  vector <int> vtasks = database_sprint.getTasks (bible, year, month);
  for (auto & id : vtasks) {
    string title = filter_string_sanitize_html (database_sprint.getTitle (id));
    int percentage = database_sprint.getComplete (id);
    tasks.append ("<tr id=\"a" + convert_to_string (id) + "\">\n");
    tasks.append ("<td><a href=\"?id=" + convert_to_string (id) + "&remove=\">" + emoji_wastebasket () + "</a></td>\n");
    tasks.append ("<td></td>\n");
    tasks.append ("<td><a href=\"?id=" + convert_to_string (id) + "&moveback=\"> « </a></td>\n");
    tasks.append ("<td>" + title + "</td>\n");
    int category_count = vcategories.size();
    float category_percentage = 100 / category_count;
    for (size_t i2 = 0; i2 < vcategories.size (); i2++) {
      int high = round ((i2 + 1) * category_percentage);
      tasks.append ("<td>\n");
      tasks.append ("<input type=\"checkbox\" id=\"task");
      tasks.append (convert_to_string (id));
      tasks.append ("box");
      tasks.append (convert_to_string (i2));
      tasks.append ("\"");
      if (percentage >= high)
        tasks.append (" checked");
      else
        tasks.append ("");
      tasks.append (">");
      
      tasks.append ("</td>\n");
    }
    tasks.append ("<td><a href=\"?id=" + convert_to_string (id) + "&moveforward=\"> » </a></td>\n");
    tasks.append ("</tr>\n");
  }
  view.set_variable ("tasks", tasks);

  
  view.set_variable ("chart", sprint_create_burndown_chart (bible, year, month));
                                                                                                                                                                                                                  

  page += view.render ("sprint", "index");
  page += Assets_Page::footer ();
  return page;
}