From a75efcb7af6dc469c326021155af14fb9894e274 Mon Sep 17 00:00:00 2001 From: Danil Kislov Date: Wed, 27 Mar 2019 10:14:45 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B2=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D1=8C=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B2=D0=BD?= =?UTF-8?q?=D0=B5=D1=81=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B2=D1=80=D1=83?= =?UTF-8?q?=D1=87=D0=BD=D1=83=D1=8E.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .semver.txt | 2 +- README.md | 5 +- src/JiraTimeBot/Configuration/Settings.cs | 1 + src/JiraTimeBot/JiraIntegration/JiraApi.cs | 38 +++++++++++- .../JiraIntegration/JiraCommitEmulator.cs | 1 + src/JiraTimeBot/Job.cs | 2 +- src/JiraTimeBot/Properties/AssemblyInfo.cs | 62 +++++++++---------- .../TaskTime/TaskTimeByCommitsCalculator.cs | 12 ++++ .../TasksProcessors/ITasksProcessor.cs | 3 +- .../TasksProcessors/MeetingProcessor.cs | 3 +- .../TasksProcessors/WorkLogTasksProcessor.cs | 5 +- src/JiraTimeBot/UI/frmSettings.Designer.cs | 17 ++++- src/JiraTimeBot/UI/frmSettings.cs | 8 +-- 13 files changed, 113 insertions(+), 46 deletions(-) diff --git a/.semver.txt b/.semver.txt index ffcbe71..28dff43 100644 --- a/.semver.txt +++ b/.semver.txt @@ -1 +1 @@ -1.0.34 +1.0.35 diff --git a/README.md b/README.md index 6b2161a..f422a19 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,13 @@ - Путь до репо (Режим Mercurial) - путь до папки, в которую клонируются исходники. - Логин Jira - Логин в Jira (от этого пользователя будем выполнять запросы и вносить время). - Пароль Jira - шифруется через DPAPI. - - Mercurial Name (Режим Mercurial) - обязателен к указанию для режима Mercurial - имя пользователя Mercurial, от которого проходят коммиты. + - Mercurial Name (Режим Mercurial) - обязателен к указанию для режима Mercurial - имя пользователя Mercurial, от которого проходят коммиты (именно UserName, не E-Mail). - Округление времени - Для красоты распределения времени тут устанавливается округление. По умолчанию - 15 минут, получим время 1:15, 0:30 итд. Остаток нераспределенного времени будет внесен в самую долгую задачу или же если это невозможно - количество рабочего времени будет увеличено или уменьшено. - JQL (Режим Jira) - можно задать свой JQL для поиска задач. Возможные замены: %USER% - Логин Jira, %DATE% дата на которую формируем список. Значнеие по умолчанию: status changed by '%USER%' during ("%DATE%","%DATE%") + - Задача "учёта времени" - задача, куда будет списываться по 30 минут в день на "затраты на учет времени". Должна содержать знак '-'. - Время запуска - время автоматического запуска бота. Если была установлена галочка "тестовый прогон" - она будет снята. - Добавлять описание (Режим Mercurial) - добавлять ли в описание CommitMessage, если не установлено - добавляются стандартные фразы программистов (типа "написание кода"). - - Задача "учёта времени" - задача, куда будет списываться по 30 минут в день на "затраты на учет времени". Должна содержать знак '-'. + - Заменять время за день - если галочка установлена, то время внесенное пользователем за день будет удалено и заменено на добавленное ботом. # Скачать Скачать актуальную версию можно тут: https://github.com/monster1025/JiraTimeBot/releases/latest diff --git a/src/JiraTimeBot/Configuration/Settings.cs b/src/JiraTimeBot/Configuration/Settings.cs index b0791e5..708eb6d 100644 --- a/src/JiraTimeBot/Configuration/Settings.cs +++ b/src/JiraTimeBot/Configuration/Settings.cs @@ -23,6 +23,7 @@ namespace JiraTimeBot.Configuration public WorkType WorkType { get; set; } public string JiraQuery { get; set; } public string TimeControlTask { get; set; } + public bool RemoveWorklogsAddedByUser { get; set; } public static Settings LoadAndCheck(Action settingsSource, Action errorMessageReporter) { diff --git a/src/JiraTimeBot/JiraIntegration/JiraApi.cs b/src/JiraTimeBot/JiraIntegration/JiraApi.cs index 5e7fae4..b7e4b2e 100644 --- a/src/JiraTimeBot/JiraIntegration/JiraApi.cs +++ b/src/JiraTimeBot/JiraIntegration/JiraApi.cs @@ -62,7 +62,37 @@ namespace JiraTimeBot.JiraIntegration } } - public void SetTodayWorklog(List taskTimeItems, Settings settings, DateTime? date = null, bool dummy = false, bool addCommentsToWorklog = false) + public List GetWorkloggedIssuesByDate(Settings settings, DateTime? date = null, CancellationToken cancellationToken = default(CancellationToken)) + { + date = date.GetValueOrDefault(DateTime.Now.Date); + var jql = "issueFunction in workLogged(\"on %DATE% by '%USER%'\")"; + return GetIssuesByJQL(jql, settings, date, cancellationToken); + } + + private void RemoveWorklogsAddedByUser(List taskTimeItems, Settings settings, DateTime? date = null, bool dummy = false, CancellationToken cancellationToken = default(CancellationToken)) + { + var alreadyLoggedToday = GetWorkloggedIssuesByDate(settings, date); + foreach (var issue in alreadyLoggedToday) + { + if (!taskTimeItems.Any(f => f.Branch.Equals(issue.Key.Value, StringComparison.InvariantCulture))) + { + _log.Trace($"Удаляю добавленный руками WorkLog для задачи {issue.Key.Value}."); + var workLogs = issue.GetWorklogsAsync(cancellationToken).Result; + var userWorklogs = workLogs.Where(w => + w.CreateDate.GetValueOrDefault().Date == date && w.Author.Equals(settings.JiraUserName, + StringComparison.InvariantCultureIgnoreCase)).ToList(); + foreach (var userWorklog in userWorklogs) + { + if (!dummy) + { + issue.DeleteWorklogAsync(userWorklog, token: cancellationToken); + } + } + } + } + } + + public void SetTodayWorklog(List taskTimeItems, Settings settings, DateTime? date = null, bool dummy = false, bool addCommentsToWorklog = false, CancellationToken cancellationToken = default(CancellationToken)) { var jira = Jira.CreateRestClient(settings.JiraUrl, settings.JiraUserName, settings.JiraPassword); @@ -72,6 +102,12 @@ namespace JiraTimeBot.JiraIntegration } date = date.Value.Date; + //Удаляем добавленные вручную пользователем данные. + if (settings.AddCommentsToWorklog) + { + RemoveWorklogsAddedByUser(taskTimeItems, settings, date, dummy, cancellationToken); + } + foreach (TaskTimeItem taskTimeItem in taskTimeItems) { Issue issue = null; diff --git a/src/JiraTimeBot/JiraIntegration/JiraCommitEmulator.cs b/src/JiraTimeBot/JiraIntegration/JiraCommitEmulator.cs index 42fae56..db31fcf 100644 --- a/src/JiraTimeBot/JiraIntegration/JiraCommitEmulator.cs +++ b/src/JiraTimeBot/JiraIntegration/JiraCommitEmulator.cs @@ -37,6 +37,7 @@ namespace JiraTimeBot.JiraIntegration var comments = issue.GetCommentsAsync(cancellationToken).Result?.ToList() ?? new List(); var user = settings.JiraUserName; + //user = "Zoya.Aleksandridi"; var userComments = comments.Where(f => f.Author.Equals(user, StringComparison.InvariantCultureIgnoreCase)).ToList(); _log.Trace($"Получены комментарии по задаче {issue.Key} ({issue.Type?.Name}): {userComments.Count}."); diff --git a/src/JiraTimeBot/Job.cs b/src/JiraTimeBot/Job.cs index f65407a..d98161c 100644 --- a/src/JiraTimeBot/Job.cs +++ b/src/JiraTimeBot/Job.cs @@ -71,7 +71,7 @@ namespace JiraTimeBot continue; } - tasksProcessor.Process(date, taskTimes, settings, dummyMode); + tasksProcessor.Process(date, taskTimes, settings, dummyMode, cancellationToken); _log.Info("Готово."); return; diff --git a/src/JiraTimeBot/Properties/AssemblyInfo.cs b/src/JiraTimeBot/Properties/AssemblyInfo.cs index 7bb874d..fe8b321 100644 --- a/src/JiraTimeBot/Properties/AssemblyInfo.cs +++ b/src/JiraTimeBot/Properties/AssemblyInfo.cs @@ -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.34")] -[assembly: AssemblyFileVersion("1.0.34")] -[assembly: AssemblyInformationalVersion("1.0.34")] +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.35")] +[assembly: AssemblyFileVersion("1.0.35")] +[assembly: AssemblyInformationalVersion("1.0.35")] diff --git a/src/JiraTimeBot/TaskTime/TaskTimeByCommitsCalculator.cs b/src/JiraTimeBot/TaskTime/TaskTimeByCommitsCalculator.cs index 3e39b11..066679d 100644 --- a/src/JiraTimeBot/TaskTime/TaskTimeByCommitsCalculator.cs +++ b/src/JiraTimeBot/TaskTime/TaskTimeByCommitsCalculator.cs @@ -104,9 +104,21 @@ namespace JiraTimeBot.TaskTime workTimeItems.First().Time += TimeSpan.FromMinutes(remainMinutes); } + PrintTotal(workTimeItems); + return workTimeItems; } + private void PrintTotal(List workTimeItems) + { + var totalTime = TimeSpan.Zero; + foreach (var workTimeItem in workTimeItems) + { + totalTime += workTimeItem.Time; + } + + _log.Trace($"Итоговое реально проставляемое время: {totalTime}"); + } private decimal RoundTo(decimal value, decimal to = 15, bool up = true) diff --git a/src/JiraTimeBot/TasksProcessors/ITasksProcessor.cs b/src/JiraTimeBot/TasksProcessors/ITasksProcessor.cs index 9cf48ed..23968f2 100644 --- a/src/JiraTimeBot/TasksProcessors/ITasksProcessor.cs +++ b/src/JiraTimeBot/TasksProcessors/ITasksProcessor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using JiraTimeBot.Configuration; using JiraTimeBot.TaskTime.Objects; @@ -7,6 +8,6 @@ namespace JiraTimeBot.TasksProcessors { interface ITasksProcessor { - void Process(DateTime date, List taskTimes, Settings settings, bool dummyMode); + void Process(DateTime date, List taskTimes, Settings settings, bool dummyMode, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/src/JiraTimeBot/TasksProcessors/MeetingProcessor.cs b/src/JiraTimeBot/TasksProcessors/MeetingProcessor.cs index 13a3481..4003944 100644 --- a/src/JiraTimeBot/TasksProcessors/MeetingProcessor.cs +++ b/src/JiraTimeBot/TasksProcessors/MeetingProcessor.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using JiraTimeBot.Configuration; using JiraTimeBot.JiraIntegration; using JiraTimeBot.TaskTime.Objects; @@ -18,7 +19,7 @@ namespace JiraTimeBot.TasksProcessors _jiraApi = jiraApi; } - public void Process(DateTime date, List taskTimes, Settings settings, bool dummyMode) + public void Process(DateTime date, List taskTimes, Settings settings, bool dummyMode, CancellationToken cancellationToken = default(CancellationToken)) { _log.Trace($"На реальную дату {date:dd.MM.yyyy} распределение по задачам:"); diff --git a/src/JiraTimeBot/TasksProcessors/WorkLogTasksProcessor.cs b/src/JiraTimeBot/TasksProcessors/WorkLogTasksProcessor.cs index 9a5ea57..f1cc39c 100644 --- a/src/JiraTimeBot/TasksProcessors/WorkLogTasksProcessor.cs +++ b/src/JiraTimeBot/TasksProcessors/WorkLogTasksProcessor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using JiraTimeBot.Configuration; using JiraTimeBot.JiraIntegration; using JiraTimeBot.TaskTime.Objects; @@ -17,7 +18,7 @@ namespace JiraTimeBot.TasksProcessors _jiraApi = jiraApi; } - public void Process(DateTime date, List taskTimes, Settings settings, bool dummyMode) + public void Process(DateTime date, List taskTimes, Settings settings, bool dummyMode, CancellationToken cancellationToken = default(CancellationToken)) { _log.Trace($"На реальную дату {date:dd.MM.yyyy} распределение по задачам:"); foreach (var taskTime in taskTimes) @@ -25,7 +26,7 @@ namespace JiraTimeBot.TasksProcessors _log.Trace($"- {taskTime.Branch} (коммитов {taskTime.Commits}): {taskTime.Time}"); } - _jiraApi.SetTodayWorklog(taskTimes, settings, dummy: dummyMode, addCommentsToWorklog: settings.AddCommentsToWorklog); + _jiraApi.SetTodayWorklog(taskTimes, settings, dummy: dummyMode, addCommentsToWorklog: settings.AddCommentsToWorklog, cancellationToken: cancellationToken); } } } diff --git a/src/JiraTimeBot/UI/frmSettings.Designer.cs b/src/JiraTimeBot/UI/frmSettings.Designer.cs index f5ba9ce..ce6f6ca 100644 --- a/src/JiraTimeBot/UI/frmSettings.Designer.cs +++ b/src/JiraTimeBot/UI/frmSettings.Designer.cs @@ -49,6 +49,7 @@ this.label8 = new System.Windows.Forms.Label(); this.txtTimeControlTask = new System.Windows.Forms.TextBox(); this.chkAutostart = new System.Windows.Forms.CheckBox(); + this.chkRemoveAddedByUser = new System.Windows.Forms.CheckBox(); this.SuspendLayout(); // // label5 @@ -162,7 +163,7 @@ // // btnSave // - this.btnSave.Location = new System.Drawing.Point(72, 310); + this.btnSave.Location = new System.Drawing.Point(92, 322); this.btnSave.Name = "btnSave"; this.btnSave.Size = new System.Drawing.Size(75, 23); this.btnSave.TabIndex = 32; @@ -234,11 +235,22 @@ this.chkAutostart.Text = "Автозагрузка"; this.chkAutostart.UseVisualStyleBackColor = true; // + // chkRemoveAddedByUser + // + this.chkRemoveAddedByUser.AutoSize = true; + this.chkRemoveAddedByUser.Location = new System.Drawing.Point(56, 299); + this.chkRemoveAddedByUser.Name = "chkRemoveAddedByUser"; + this.chkRemoveAddedByUser.Size = new System.Drawing.Size(153, 17); + this.chkRemoveAddedByUser.TabIndex = 41; + this.chkRemoveAddedByUser.Text = "Заменять время за день"; + this.chkRemoveAddedByUser.UseVisualStyleBackColor = true; + // // frmSettings // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(243, 342); + this.ClientSize = new System.Drawing.Size(243, 357); + this.Controls.Add(this.chkRemoveAddedByUser); this.Controls.Add(this.chkAutostart); this.Controls.Add(this.label8); this.Controls.Add(this.txtTimeControlTask); @@ -294,5 +306,6 @@ private System.Windows.Forms.Label label8; private System.Windows.Forms.TextBox txtTimeControlTask; private System.Windows.Forms.CheckBox chkAutostart; + private System.Windows.Forms.CheckBox chkRemoveAddedByUser; } } \ No newline at end of file diff --git a/src/JiraTimeBot/UI/frmSettings.cs b/src/JiraTimeBot/UI/frmSettings.cs index 3052cd8..cba1c0f 100644 --- a/src/JiraTimeBot/UI/frmSettings.cs +++ b/src/JiraTimeBot/UI/frmSettings.cs @@ -33,11 +33,11 @@ namespace JiraTimeBot.UI txtMercurialEmail.Text = settings.MercurialAuthorEmail; txtRepoPath.Text = settings.RepositoryPath; actTime.Text = settings.ActivationTime.ToString("hh\\:mm\\:ss"); - //txtDummyMode.Checked = settings.DummyMode; chkAddComments.Checked = settings.AddCommentsToWorklog; txtRoundTo.Text = settings.RountToMinutes.ToString(); cboWorkType.SelectedIndex = (int) settings.WorkType; txtTimeControlTask.Text = settings.TimeControlTask; + chkRemoveAddedByUser.Checked = settings.RemoveWorklogsAddedByUser; txtJQL.Text = settings.JiraQuery; txtJQL.Enabled = settings.WorkType == WorkType.JiraLogs; } @@ -56,14 +56,14 @@ namespace JiraTimeBot.UI MercurialAuthorEmail = txtMercurialEmail.Text, ActivationTime = TimeSpan.Parse(actTime.Text), RepositoryPath = txtRepoPath.Text, - //DummyMode = txtDummyMode.Checked, AddCommentsToWorklog = chkAddComments.Checked, RountToMinutes = roundTo, WorkType = (WorkType) cboWorkType.SelectedIndex, JiraQuery = txtJQL.Text, - TimeControlTask = txtTimeControlTask.Text + TimeControlTask = txtTimeControlTask.Text, + RemoveWorklogsAddedByUser = chkRemoveAddedByUser.Checked }; - + //LockUnlock(false); return settings;