You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
6.2 KiB

  1. #include <string>
  2. #include <sstream>
  3. #include <iostream>
  4. #include <fstream>
  5. #include <iomanip>
  6. #include <map>
  7. #include <list>
  8. using namespace std;
  9. // this function takes a line that may contain a name and/or email address,
  10. // and returns just the name, while fixing the "bad cases".
  11. std::string contributor_name(const std::string& line)
  12. {
  13. string result;
  14. // let's first take care of the case of isolated email addresses, like
  15. // "user@localhost.localdomain" entries
  16. if(line.find("markb@localhost.localdomain") != string::npos)
  17. {
  18. return "Mark Borgerding";
  19. }
  20. if(line.find("kayhman@contact.intra.cea.fr") != string::npos)
  21. {
  22. return "Guillaume Saupin";
  23. }
  24. // from there on we assume that we have a entry of the form
  25. // either:
  26. // Bla bli Blurp
  27. // or:
  28. // Bla bli Blurp <bblurp@email.com>
  29. size_t position_of_email_address = line.find_first_of('<');
  30. if(position_of_email_address != string::npos)
  31. {
  32. // there is an e-mail address in <...>.
  33. // Hauke once committed as "John Smith", fix that.
  34. if(line.find("hauke.heibel") != string::npos)
  35. result = "Hauke Heibel";
  36. else
  37. {
  38. // just remove the e-mail address
  39. result = line.substr(0, position_of_email_address);
  40. }
  41. }
  42. else
  43. {
  44. // there is no e-mail address in <...>.
  45. if(line.find("convert-repo") != string::npos)
  46. result = "";
  47. else
  48. result = line;
  49. }
  50. // remove trailing spaces
  51. size_t length = result.length();
  52. while(length >= 1 && result[length-1] == ' ') result.erase(--length);
  53. return result;
  54. }
  55. // parses hg churn output to generate a contributors map.
  56. map<string,int> contributors_map_from_churn_output(const char *filename)
  57. {
  58. map<string,int> contributors_map;
  59. string line;
  60. ifstream churn_out;
  61. churn_out.open(filename, ios::in);
  62. while(!getline(churn_out,line).eof())
  63. {
  64. // remove the histograms "******" that hg churn may draw at the end of some lines
  65. size_t first_star = line.find_first_of('*');
  66. if(first_star != string::npos) line.erase(first_star);
  67. // remove trailing spaces
  68. size_t length = line.length();
  69. while(length >= 1 && line[length-1] == ' ') line.erase(--length);
  70. // now the last space indicates where the number starts
  71. size_t last_space = line.find_last_of(' ');
  72. // get the number (of changesets or of modified lines for each contributor)
  73. int number;
  74. istringstream(line.substr(last_space+1)) >> number;
  75. // get the name of the contributor
  76. line.erase(last_space);
  77. string name = contributor_name(line);
  78. map<string,int>::iterator it = contributors_map.find(name);
  79. // if new contributor, insert
  80. if(it == contributors_map.end())
  81. contributors_map.insert(pair<string,int>(name, number));
  82. // if duplicate, just add the number
  83. else
  84. it->second += number;
  85. }
  86. churn_out.close();
  87. return contributors_map;
  88. }
  89. // find the last name, i.e. the last word.
  90. // for "van den Schbling" types of last names, that's not a problem, that's actually what we want.
  91. string lastname(const string& name)
  92. {
  93. size_t last_space = name.find_last_of(' ');
  94. if(last_space >= name.length()-1) return name;
  95. else return name.substr(last_space+1);
  96. }
  97. struct contributor
  98. {
  99. string name;
  100. int changedlines;
  101. int changesets;
  102. string url;
  103. string misc;
  104. contributor() : changedlines(0), changesets(0) {}
  105. bool operator < (const contributor& other)
  106. {
  107. return lastname(name).compare(lastname(other.name)) < 0;
  108. }
  109. };
  110. void add_online_info_into_contributors_list(list<contributor>& contributors_list, const char *filename)
  111. {
  112. string line;
  113. ifstream online_info;
  114. online_info.open(filename, ios::in);
  115. while(!getline(online_info,line).eof())
  116. {
  117. string hgname, realname, url, misc;
  118. size_t last_bar = line.find_last_of('|');
  119. if(last_bar == string::npos) continue;
  120. if(last_bar < line.length())
  121. misc = line.substr(last_bar+1);
  122. line.erase(last_bar);
  123. last_bar = line.find_last_of('|');
  124. if(last_bar == string::npos) continue;
  125. if(last_bar < line.length())
  126. url = line.substr(last_bar+1);
  127. line.erase(last_bar);
  128. last_bar = line.find_last_of('|');
  129. if(last_bar == string::npos) continue;
  130. if(last_bar < line.length())
  131. realname = line.substr(last_bar+1);
  132. line.erase(last_bar);
  133. hgname = line;
  134. // remove the example line
  135. if(hgname.find("MercurialName") != string::npos) continue;
  136. list<contributor>::iterator it;
  137. for(it=contributors_list.begin(); it != contributors_list.end() && it->name != hgname; ++it)
  138. {}
  139. if(it == contributors_list.end())
  140. {
  141. contributor c;
  142. c.name = realname;
  143. c.url = url;
  144. c.misc = misc;
  145. contributors_list.push_back(c);
  146. }
  147. else
  148. {
  149. it->name = realname;
  150. it->url = url;
  151. it->misc = misc;
  152. }
  153. }
  154. }
  155. int main()
  156. {
  157. // parse the hg churn output files
  158. map<string,int> contributors_map_for_changedlines = contributors_map_from_churn_output("churn-changedlines.out");
  159. //map<string,int> contributors_map_for_changesets = contributors_map_from_churn_output("churn-changesets.out");
  160. // merge into the contributors list
  161. list<contributor> contributors_list;
  162. map<string,int>::iterator it;
  163. for(it=contributors_map_for_changedlines.begin(); it != contributors_map_for_changedlines.end(); ++it)
  164. {
  165. contributor c;
  166. c.name = it->first;
  167. c.changedlines = it->second;
  168. c.changesets = 0; //contributors_map_for_changesets.find(it->first)->second;
  169. contributors_list.push_back(c);
  170. }
  171. add_online_info_into_contributors_list(contributors_list, "online-info.out");
  172. contributors_list.sort();
  173. cout << "{| cellpadding=\"5\"\n";
  174. cout << "!\n";
  175. cout << "! Lines changed\n";
  176. cout << "!\n";
  177. list<contributor>::iterator itc;
  178. int i = 0;
  179. for(itc=contributors_list.begin(); itc != contributors_list.end(); ++itc)
  180. {
  181. if(itc->name.length() == 0) continue;
  182. if(i%2) cout << "|-\n";
  183. else cout << "|- style=\"background:#FFFFD0\"\n";
  184. if(itc->url.length())
  185. cout << "| [" << itc->url << " " << itc->name << "]\n";
  186. else
  187. cout << "| " << itc->name << "\n";
  188. if(itc->changedlines)
  189. cout << "| " << itc->changedlines << "\n";
  190. else
  191. cout << "| (no information)\n";
  192. cout << "| " << itc->misc << "\n";
  193. i++;
  194. }
  195. cout << "|}" << endl;
  196. }