Compare commits

..
7 Commits
11 changed files with 121 additions and 77 deletions
+1 -1
View File
@@ -1 +1 @@
1.0.23
1.0.27
+3 -2
View File
@@ -14,9 +14,10 @@
- Пароль Jira - шифруется через DPAPI.
- Mercurial Name (Режим Mercurial) - обязателен к указанию для режима Mercurial - имя пользователя Mercurial, от которого проходят коммиты.
- Округление времени - Для красоты распределения времени тут устанавливается округление. По умолчанию - 15 минут, получим время 1:15, 0:30 итд. Остаток нераспределенного времени будет внесен в самую долгую задачу или же если это невозможно - количество рабочего времени будет увеличено или уменьшено.
- JQL (Режим Jira) - можно задать свой JQL для поиска задачь. Возможные замены: %USER% - Логин Jira, %DATE% дата на которую формируем список. Значнеие по умолчанию: status changed by '%USER%' during ("%DATE%","%DATE%")
- JQL (Режим Jira) - можно задать свой JQL для поиска задач. Возможные замены: %USER% - Логин Jira, %DATE% дата на которую формируем список. Значнеие по умолчанию: status changed by '%USER%' during ("%DATE%","%DATE%")
- Время запуска - время автоматического запуска бота. Если была установлена галочка "тестовый прогон" - она будет снята.
- Добавлять описание (Режим Mercurial) - добавлять ли в описание CommitMessage, если не установлено - добавляются стандартные фразы программистов (типа "написание кода").
- Задача "учёта времени" - задача, куда будет списываться по 30 минут в день на "затраты на учет времени". Должна содержать знак '-'.
# Скачать
Скачать актуальную версию можно тут: https://github.com/monster1025/JiraTimeBot/releases/latest
+7 -1
View File
@@ -22,6 +22,7 @@ namespace JiraTimeBot.Configuration
public bool AddCommentsToWorklog { get; set; }
public WorkType WorkType { get; set; }
public string JiraQuery { get; set; }
public string TimeControlTask { get; set; }
public static Settings LoadAndCheck(Action settingsSource, Action<string> errorMessageReporter)
{
@@ -61,7 +62,7 @@ namespace JiraTimeBot.Configuration
if (settings.RountToMinutes == default(int))
{
errors.Add("Укажите округление времени, например, 10 минут");
errors.Add("Укажите округление времени, например, 10 (минут)");
}
if (string.IsNullOrEmpty(settings.JiraPassword))
@@ -79,6 +80,11 @@ namespace JiraTimeBot.Configuration
errors.Add("Укажите верный RepositoryPath");
}
if (!string.IsNullOrEmpty(settings.TimeControlTask) && !settings.TimeControlTask.Contains("-"))
{
errors.Add("Укажите верно зажачу контроля времени. Она джолжна содержать знак '-', например, SV-1211");
}
var hasErrors = errors.Any();
if (hasErrors)
{
@@ -34,20 +34,11 @@ namespace JiraTimeBot.JiraIntegration
{
return new List<MercurialCommitItem>();
}
List<Comment> comments = new List<Comment>();
if (issue.Type?.Name != "Дубликат")
{
_log.Trace($"Получаю комментарии по задаче {issue.Key} ({issue.Type?.Name}).");
comments = issue.GetCommentsAsync(cancellationToken).Result?.ToList() ?? new List<Comment>();
}
else
{
_log.Trace($"Пропускаю комментарии по задаче {issue.Key} ({issue.Type?.Name}).");
}
var comments = issue.GetCommentsAsync(cancellationToken).Result?.ToList() ?? new List<Comment>();
var user = settings.JiraUserName;
var userComments = comments.Where(f => f.Author.Equals(user, StringComparison.InvariantCultureIgnoreCase)).ToList();
_log.Trace($"Получены комментарии по задаче {issue.Key} ({issue.Type?.Name}): {userComments.Count}.");
if (userComments.Any())
{
+5 -3
View File
@@ -1,5 +1,7 @@
using JiraTimeBot.UI;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
@@ -7,19 +9,19 @@ namespace JiraTimeBot
{
static class Program
{
private static Mutex _mutex = new Mutex(true, "{44426B3E-B901-4792-ACEA-1385D79DBAD1}");
private static readonly Mutex Mutex = new Mutex(true, "{44426B3E-B901-4792-ACEA-1385D79DBAD1}");
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
if (_mutex.WaitOne(TimeSpan.Zero, true))
if (Mutex.WaitOne(TimeSpan.Zero, true))
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmMain());
_mutex.ReleaseMutex();
Mutex.ReleaseMutex();
}
else
{
+31 -31
View File
@@ -1,31 +1,31 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("JiraTimeBot")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("JiraTimeBot")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("f3b89316-c38c-4ce6-9d88-0b1373cfba1a")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.23")]
[assembly: AssemblyFileVersion("1.0.23")]
[assembly: AssemblyInformationalVersion("1.0.23")]
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("JiraTimeBot")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("JiraTimeBot")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("f3b89316-c38c-4ce6-9d88-0b1373cfba1a")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.27")]
[assembly: AssemblyFileVersion("1.0.27")]
[assembly: AssemblyInformationalVersion("1.0.27")]
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Web.UI.WebControls.WebParts;
using JiraTimeBot.Configuration;
using JiraTimeBot.Mercurial.Objects;
using JiraTimeBot.TaskTime.Objects;
@@ -20,14 +21,20 @@ namespace JiraTimeBot.TaskTime
public List<TaskTimeItem> CalculateTaskTime(List<MercurialCommitItem> commits, Settings settings, CancellationToken cancellationToken = default(CancellationToken))
{
int minutesPerWorkDay = settings.MinuterPerWorkDay;
int remainMinutes = settings.MinuterPerWorkDay;
int totalCommitsCount = commits.Count;
if (!commits.Any())
{
return new List<TaskTimeItem>();
}
//если кол-во коммитов более чем кол-во интервалов - то уменьшим интервал вдвое.
while (totalCommitsCount > (8 * (60.0 / settings.RountToMinutes)))
{
settings.RountToMinutes = (int)RoundTo((decimal)(settings.RountToMinutes / 2.0), 5);
_log.Info($"Слишком много задачь - уменьшаю интервал до {settings.RountToMinutes}.");
_log.Info($"Слишком много задач - уменьшаю интервал до {settings.RountToMinutes}.");
if (settings.RountToMinutes == 5)
{
break;
@@ -35,6 +42,19 @@ namespace JiraTimeBot.TaskTime
}
List<TaskTimeItem> workTimeItems = new List<TaskTimeItem>();
//Если указана задача контроля времени - то спишем туда 30 минут и вычеркнем их из общего рабочего времени.
if (!string.IsNullOrEmpty(settings.TimeControlTask))
{
minutesPerWorkDay = minutesPerWorkDay - 30;
workTimeItems.Add(new TaskTimeItem
{
Time = TimeSpan.FromMinutes(30),
Branch = settings.TimeControlTask,
Commits = 1,
Description = "Время на учет задач",
StartTime = DateTime.Now.Date
});
}
//Нам нужно раскидать 480 минут в день.
foreach (var taskGroup in commits.GroupBy(f => f.Branch).OrderByDescending(f=>f.Count()))
@@ -45,7 +65,7 @@ namespace JiraTimeBot.TaskTime
}
int currentTaskCommits = taskGroup.Count();
int currentTaskTime = (int)RoundTo(settings.MinuterPerWorkDay / totalCommitsCount * currentTaskCommits, settings.RountToMinutes);
int currentTaskTime = (int)RoundTo(minutesPerWorkDay / totalCommitsCount * currentTaskCommits, settings.RountToMinutes);
remainMinutes = remainMinutes - currentTaskTime;
var orderedTasks = taskGroup.OrderBy(f => f.Time).ToArray();
@@ -72,6 +92,7 @@ namespace JiraTimeBot.TaskTime
{
return new List<TaskTimeItem>();
}
workTimeItems = workTimeItems.OrderBy(f => f.Time).ToList();
if (remainMinutes != 0)
{
+6 -4
View File
@@ -29,6 +29,7 @@ namespace JiraTimeBot.UI
private Settings _settings;
private readonly Action _settingsWindowShow;
private readonly Action<string> _settingsErrorReporter;
private bool _updateChecked;
private CancellationTokenSource GetTokenSource()
{
@@ -61,7 +62,6 @@ namespace JiraTimeBot.UI
var frmSettings = new frmSettings();
frmSettings.ShowDialog(this);
};
_settingsErrorReporter = msg => MessageBox.Show(msg, "Загрузка настроек", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);;
}
@@ -200,7 +200,9 @@ namespace JiraTimeBot.UI
{
try
{
tmrUpdate.Interval = 8 * 60 * 60 * 1000;
var firstTime = !_updateChecked;
_updateChecked = true;
tmrUpdate.Interval = 1 * 60 * 60 * 1000;
var checker = new UpdateChecker();
var release = checker.GetRelease();
@@ -231,9 +233,9 @@ namespace JiraTimeBot.UI
tmrUpdate.Enabled = false;
}
}
else
else if (firstTime)
{
_log.Info($"Вы используете последнюю версию.");
_log.Info($"Вы используете актуальную версию.");
}
}
catch (Exception ex)
+41 -19
View File
@@ -46,12 +46,14 @@
this.label6 = new System.Windows.Forms.Label();
this.label7 = new System.Windows.Forms.Label();
this.txtJQL = new System.Windows.Forms.TextBox();
this.label8 = new System.Windows.Forms.Label();
this.txtTimeControlTask = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(7, 153);
this.label5.Location = new System.Drawing.Point(33, 153);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(100, 13);
this.label5.TabIndex = 28;
@@ -59,7 +61,7 @@
//
// txtRoundTo
//
this.txtRoundTo.Location = new System.Drawing.Point(109, 150);
this.txtRoundTo.Location = new System.Drawing.Point(133, 150);
this.txtRoundTo.Name = "txtRoundTo";
this.txtRoundTo.Size = new System.Drawing.Size(100, 20);
this.txtRoundTo.TabIndex = 27;
@@ -68,7 +70,7 @@
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(27, 48);
this.label4.Location = new System.Drawing.Point(53, 48);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(76, 13);
this.label4.TabIndex = 26;
@@ -76,7 +78,7 @@
//
// txtRepoPath
//
this.txtRepoPath.Location = new System.Drawing.Point(109, 45);
this.txtRepoPath.Location = new System.Drawing.Point(133, 45);
this.txtRepoPath.Name = "txtRepoPath";
this.txtRepoPath.Size = new System.Drawing.Size(100, 20);
this.txtRepoPath.TabIndex = 25;
@@ -84,7 +86,7 @@
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(21, 127);
this.label2.Location = new System.Drawing.Point(47, 127);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(82, 13);
this.label2.TabIndex = 24;
@@ -92,7 +94,7 @@
//
// txtMercurialEmail
//
this.txtMercurialEmail.Location = new System.Drawing.Point(109, 124);
this.txtMercurialEmail.Location = new System.Drawing.Point(133, 124);
this.txtMercurialEmail.Name = "txtMercurialEmail";
this.txtMercurialEmail.Size = new System.Drawing.Size(100, 20);
this.txtMercurialEmail.TabIndex = 23;
@@ -100,7 +102,7 @@
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(36, 100);
this.label1.Location = new System.Drawing.Point(62, 100);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(71, 13);
this.label1.TabIndex = 22;
@@ -108,7 +110,7 @@
//
// txtJiraPassword
//
this.txtJiraPassword.Location = new System.Drawing.Point(109, 97);
this.txtJiraPassword.Location = new System.Drawing.Point(133, 97);
this.txtJiraPassword.Name = "txtJiraPassword";
this.txtJiraPassword.PasswordChar = '*';
this.txtJiraPassword.Size = new System.Drawing.Size(100, 20);
@@ -117,7 +119,7 @@
// lbl1
//
this.lbl1.AutoSize = true;
this.lbl1.Location = new System.Drawing.Point(43, 74);
this.lbl1.Location = new System.Drawing.Point(69, 74);
this.lbl1.Name = "lbl1";
this.lbl1.Size = new System.Drawing.Size(64, 13);
this.lbl1.TabIndex = 20;
@@ -125,7 +127,7 @@
//
// txtJiraLogin
//
this.txtJiraLogin.Location = new System.Drawing.Point(109, 71);
this.txtJiraLogin.Location = new System.Drawing.Point(133, 71);
this.txtJiraLogin.Name = "txtJiraLogin";
this.txtJiraLogin.Size = new System.Drawing.Size(100, 20);
this.txtJiraLogin.TabIndex = 19;
@@ -133,7 +135,7 @@
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(16, 206);
this.label3.Location = new System.Drawing.Point(41, 232);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(91, 13);
this.label3.TabIndex = 30;
@@ -142,7 +144,7 @@
// actTime
//
this.actTime.Format = System.Windows.Forms.DateTimePickerFormat.Time;
this.actTime.Location = new System.Drawing.Point(109, 202);
this.actTime.Location = new System.Drawing.Point(133, 228);
this.actTime.Name = "actTime";
this.actTime.Size = new System.Drawing.Size(100, 20);
this.actTime.TabIndex = 29;
@@ -150,7 +152,7 @@
// chkAddComments
//
this.chkAddComments.AutoSize = true;
this.chkAddComments.Location = new System.Drawing.Point(46, 242);
this.chkAddComments.Location = new System.Drawing.Point(56, 267);
this.chkAddComments.Name = "chkAddComments";
this.chkAddComments.Size = new System.Drawing.Size(133, 17);
this.chkAddComments.TabIndex = 31;
@@ -159,7 +161,7 @@
//
// btnSave
//
this.btnSave.Location = new System.Drawing.Point(61, 276);
this.btnSave.Location = new System.Drawing.Point(72, 299);
this.btnSave.Name = "btnSave";
this.btnSave.Size = new System.Drawing.Size(75, 23);
this.btnSave.TabIndex = 32;
@@ -174,7 +176,7 @@
this.cboWorkType.Items.AddRange(new object[] {
"Mercurial",
"Jira"});
this.cboWorkType.Location = new System.Drawing.Point(109, 18);
this.cboWorkType.Location = new System.Drawing.Point(133, 18);
this.cboWorkType.Name = "cboWorkType";
this.cboWorkType.Size = new System.Drawing.Size(99, 21);
this.cboWorkType.TabIndex = 34;
@@ -183,7 +185,7 @@
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(18, 21);
this.label6.Location = new System.Drawing.Point(44, 21);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(85, 13);
this.label6.TabIndex = 35;
@@ -192,7 +194,7 @@
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(74, 179);
this.label7.Location = new System.Drawing.Point(100, 179);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(29, 13);
this.label7.TabIndex = 37;
@@ -200,16 +202,34 @@
//
// txtJQL
//
this.txtJQL.Location = new System.Drawing.Point(109, 176);
this.txtJQL.Location = new System.Drawing.Point(133, 176);
this.txtJQL.Name = "txtJQL";
this.txtJQL.Size = new System.Drawing.Size(99, 20);
this.txtJQL.TabIndex = 36;
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(-2, 206);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(133, 13);
this.label8.TabIndex = 39;
this.label8.Text = "Задача \"учёта времени\":";
//
// txtTimeControlTask
//
this.txtTimeControlTask.Location = new System.Drawing.Point(133, 202);
this.txtTimeControlTask.Name = "txtTimeControlTask";
this.txtTimeControlTask.Size = new System.Drawing.Size(99, 20);
this.txtTimeControlTask.TabIndex = 38;
//
// frmSettings
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(220, 311);
this.ClientSize = new System.Drawing.Size(243, 334);
this.Controls.Add(this.label8);
this.Controls.Add(this.txtTimeControlTask);
this.Controls.Add(this.label7);
this.Controls.Add(this.txtJQL);
this.Controls.Add(this.label6);
@@ -259,5 +279,7 @@
private System.Windows.Forms.Label label6;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.TextBox txtJQL;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.TextBox txtTimeControlTask;
}
}
+2
View File
@@ -31,6 +31,7 @@ namespace JiraTimeBot.UI
chkAddComments.Checked = settings.AddCommentsToWorklog;
txtRoundTo.Text = settings.RountToMinutes.ToString();
cboWorkType.SelectedIndex = (int) settings.WorkType;
txtTimeControlTask.Text = settings.TimeControlTask;
txtJQL.Text = settings.JiraQuery;
txtJQL.Enabled = settings.WorkType == WorkType.JiraLogs;
}
@@ -54,6 +55,7 @@ namespace JiraTimeBot.UI
RountToMinutes = roundTo,
WorkType = (WorkType) cboWorkType.SelectedIndex,
JiraQuery = txtJQL.Text,
TimeControlTask = txtTimeControlTask.Text
};
//LockUnlock(false);
-3
View File
@@ -117,7 +117,4 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="cboWorkType.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
</root>