174 fprintf(stderr, "Pulling repo '%s' failed!\nMaybe there is no remote or there are local changes?\n", repo.path.c_str()); |
171 fprintf(stderr, "Pulling repo '%s' failed!\nMaybe there is no remote or there are local changes?\n", repo.path.c_str()); |
175 } |
172 } |
176 } |
173 } |
177 } |
174 } |
178 } |
175 } |
179 // TODO: calculate the heat maps |
176 |
180 |
177 // determine our reporting range |
181 print_html_header(); |
178 int year; |
182 // TODO: output the heat maps here |
179 if (settings.year == fm::settings_current_year) { |
183 print_html_footer(); |
180 year = static_cast<int>(chrono::year_month_day{chrono::floor<chrono::days>(chrono::system_clock::now())}.year()); |
|
181 } else { |
|
182 year = settings.year; |
|
183 } |
|
184 chrono::year_month_day report_begin{chrono::year{year}, chrono::month{1}, chrono::day{1}}; |
|
185 chrono::year_month_day report_end{chrono::year{year}, chrono::month{12}, chrono::day{31}}; |
|
186 |
|
187 // read the commit logs |
|
188 fm::heatmap heatmap; |
|
189 for (auto &&repo : repos.list()) { |
|
190 if (settings.separate) { |
|
191 heatmap.set_repo(repo.path); |
|
192 } |
|
193 proc.chdir(repo.path); |
|
194 if (repo.type == fm::HG) { |
|
195 proc.setbin(settings.hg); |
|
196 if (proc.exec_log({"log", |
|
197 "--date", std::format("{0}-01-01 to {0}-12-31", year), |
|
198 "--template", "{author}#{date|shortdate}\n"})) { |
|
199 fprintf(stderr, "Reading commit log for repo '%s' failed!\n", repo.path.c_str()); |
|
200 return EXIT_FAILURE; |
|
201 } |
|
202 heatmap.add(proc.output()); |
|
203 } else { |
|
204 proc.setbin(settings.git); |
|
205 if (proc.exec({"log", |
|
206 "--since", std::format("{0}-01-01", year), |
|
207 "--until", std::format("{0}-12-31", year), |
|
208 "--format=tformat:%an <%ae>#%cs"})) { |
|
209 fprintf(stderr, "Reading commit log for repo '%s' failed!\n", repo.path.c_str()); |
|
210 return EXIT_FAILURE; |
|
211 } |
|
212 heatmap.add(proc.output()); |
|
213 } |
|
214 } |
|
215 |
|
216 html::open(); |
|
217 for (const auto &[repo, authors] : heatmap.data()) { |
|
218 html::h1(repo); |
|
219 for (const auto &[author, entries] : authors) { |
|
220 html::h2(author); |
|
221 html::table_begin(); |
|
222 |
|
223 // initialize counters |
|
224 unsigned column = 0, row = 0; |
|
225 |
|
226 // initialize first day (which must be a Monday, possibly the year before) |
|
227 chrono::sys_days day_to_check{report_begin}; |
|
228 day_to_check -= chrono::days{chrono::weekday{day_to_check}.iso_encoding() - 1}; |
|
229 |
|
230 // remember the starting point |
|
231 auto start = day_to_check; |
|
232 |
|
233 // now add all entries for Monday, Tuesdays, etc. always starting back in january |
|
234 while (true) { |
|
235 html::row_begin(row); |
|
236 |
|
237 // check if we need to add blank cells |
|
238 while (day_to_check < report_begin) { |
|
239 html::cell_out_of_range(); |
|
240 day_to_check += chrono::days{7}; |
|
241 column++; |
|
242 } |
|
243 |
|
244 while (day_to_check <= report_end) { |
|
245 // get the entry from the heatmap |
|
246 auto find_result = entries.find(day_to_check); |
|
247 if (find_result == entries.end()) { |
|
248 html::cell(0); |
|
249 } else { |
|
250 html::cell(find_result->second); |
|
251 } |
|
252 // advance seven days and one column |
|
253 day_to_check += chrono::days{7}; |
|
254 column++; |
|
255 } |
|
256 // fill remaining columns with blank cells |
|
257 for (unsigned i = column ; i < html::columns ; i++) { |
|
258 html::cell_out_of_range(); |
|
259 } |
|
260 |
|
261 // terminate the row |
|
262 html::row_end(); |
|
263 |
|
264 // if we have seen all seven weekdays, that's it |
|
265 if (++row == 7) break; |
|
266 |
|
267 // otherwise, advance the starting point by one day, reset, and begin a new row |
|
268 start += chrono::days{1}; |
|
269 day_to_check = start; |
|
270 column =0; |
|
271 } |
|
272 |
|
273 html::table_end(); |
|
274 } |
|
275 } |
|
276 html::close(); |
184 |
277 |
185 return EXIT_SUCCESS; |
278 return EXIT_SUCCESS; |
186 } |
279 } |