public void AddWorkLog(string issueRef, WorkLogStrategy workLogStrategy, string comment, TimeSpan timeSpent, DateTime logDate, TimeSpan? remainingTime = null)
        {
            if (logDate.Kind != DateTimeKind.Local) logDate = DateTime.SpecifyKind(logDate, DateTimeKind.Local);

            var issue = client.GetIssue(issueRef);

            var worklog = new Worklog(string.Format("{0}h {1}m", timeSpent.Hours, timeSpent.Minutes), logDate, comment);
            string remaining = null;
            if (remainingTime.HasValue)
            {
                remaining = string.Format("{0}h {1}m", remainingTime.Value.Hours, remainingTime.Value.Minutes);
            }

            WorklogStrategy strategy;
            switch (workLogStrategy)
            {
                case WorkLogStrategy.Automatic:
                    strategy = WorklogStrategy.AutoAdjustRemainingEstimate;
                    break;
                case WorkLogStrategy.LeaveRemaining:
                    strategy = WorklogStrategy.RetainRemainingEstimate;
                    break;
                case WorkLogStrategy.SetValue:
                    strategy = WorklogStrategy.NewRemainingEstimate;
                    break;
                default:
                    strategy = WorklogStrategy.RetainRemainingEstimate;
                    break;
            }

            issue.AddWorklog(worklog, strategy, remaining);
        }
Exemple #2
0
        // needs to post to: /rest/api/2/issue/{issueIdOrKey}/worklog
        bool RecordTime(TimeEntry timeEntry, Task task)
        {
            // jira requires a special format - like ISO 8601 but not quite
            const string jiraTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffK";

            // jira doesn't like the colon in the ISO 8601 string. so we strip it out.
            var timeStarted = timeEntry.TimeRecorded.ToString(jiraTimeFormat).ReplaceLastOccurrence(":", "");

            var worklog = new Worklog
            {
                comment   = timeEntry.Comment ?? "",
                started   = timeStarted,
                timeSpent = timeEntry.MinutesSpent + "m"
            };

            var adjustEstimate = !AdjustJiraEstimate() ? "?adjustEstimate=leave": "";

            var post = new RestRequest()
            {
                Resource      = "issue/{0}/worklog{1}".FormatWith(task.Id, adjustEstimate),
                Method        = Method.POST,
                RequestFormat = DataFormat.Json
            };

            post.AddBody(worklog);

            var result = Api.Execute <Worklog>(post);

            return(result != null);
        }
        public async Task <ActionResult <Worklog> > PostWorklog(Worklog worklog)
        {
            _context.Worklogs.Add(worklog);
            await _context.SaveChangesAsync();

            return(CreatedAtAction("GetWorklog", new { id = worklog.Id }, worklog));
        }
        public ActionResult _CreateWorklog(WorklogsModels worklogToAdd)
        {
            if (ModelState.IsValid)
            {
                var             userManager = new UserManager <ApplicationUser>(new UserStore <ApplicationUser>(db));
                ApplicationUser user        = userManager.FindById(User.Identity.GetUserId());
                var             worklog     = new Worklog
                {
                    Logger       = user,
                    CreationTime = DateTime.Now,
                    LoggedTime   = worklogToAdd.LoggedTime,
                    Task         = db.Tasks.Find(worklogToAdd.TaskId)
                };
                db.Worklogs.Add(worklog);
                db.SaveChanges();
            }

            var model = new WorklogsModels
            {
                TaskId   = worklogToAdd.TaskId,
                Worklogs = db.Tasks.Find(worklogToAdd.TaskId).Worklogs
            };
            float taskTime = 0f;

            foreach (var wl in model.Worklogs)
            {
                taskTime += wl.LoggedTime;
            }
            model.TaskTime = taskTime;

            //return PartialView("_CreateWorklog", model);
            return(RedirectToAction("TaskDetails", "Projects", new { Id = worklogToAdd.TaskId }));
        }
        public async Task <IActionResult> PutWorklog(int id, Worklog worklog)
        {
            if (id != worklog.Id)
            {
                return(BadRequest());
            }

            _context.Entry(worklog).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!WorklogExists(id))
                {
                    return(NotFound());
                }
                else
                {
                    throw;
                }
            }

            return(NoContent());
        }
Exemple #6
0
        static void Main(string[] args)
        {
            var tempoClient = new WorklogService(new Uri(""));

            tempoClient.SetBasicAuthentication("", "");

            var searchResult = tempoClient.Issue.GetAsync("21127").Result;
            var myselfAsync  = tempoClient.User.GetMyselfAsync().Result;
            var worklog      = new Worklog()
            {
                Author = new Author()
                {
                    Name = myselfAsync.Name,
                    Self = myselfAsync.Self.ToString()
                },
                Issue = new Issue()
                {
                    Key = "18652"
                },
                DateStarted       = DateTime.Now.ToString("o"),
                Comment           = "Tested toggl2tempo",
                TimeSpentSeconds  = 2600,
                WorklogAttributes = new List <WorklogAttribute>()
                {
                    new WorklogAttribute()
                    {
                        Key = "_Activity_", Value = "Other"
                    }
                }
            };

            var result = tempoClient.Tempo.CreateAsync(worklog).Result;
        }
        public async Task <Worklog> UpdateWorklog(Worklog WorklogID)
        {
            string query = @"UPDATE Worklogs SET ProjectID = @ProjectID, Date = @Date, EstimateWorkTime = @EstimateWorkTime,
                       Feature = @Feature WHERE UserID = @UserID AND WorklogID = @WorklogID";


            var result = _onlineTimeTrackContext.Database.ExecuteSqlCommand(query,

                                                                            new SqlParameter("@ProjectID", WorklogID.ProjectID),
                                                                            new SqlParameter("@Date", WorklogID.Date),
                                                                            new SqlParameter("@EstimateWorkTime", WorklogID.EstimateWorkTime),
                                                                            new SqlParameter("@Feature", WorklogID.Feature),
                                                                            new SqlParameter("@UserID", WorklogID.UserID),
                                                                            new SqlParameter("@WorklogID", WorklogID.WorklogID));

            if (result == 0)
            {
                Console.Write("Invalid User");
            }
            else

            if (result == 1)
            {
                Console.Write("Succesfully Updated");
            }


            var ExistingWorklog = _onlineTimeTrackContext.Worklogs.FirstOrDefault(x => x.WorklogID == WorklogID.WorklogID);
            await _onlineTimeTrackContext.SaveChangesAsync();

            return(ExistingWorklog);
        }
Exemple #8
0
        private void StopTracking()
        {
            if (_currentWorklog == null)
            {
                return;
            }

            var timeSpent = (int)DateTime.Now.Subtract(_currentWorklog.Started).TotalSeconds;

            _currentWorklog.TimeSpentInSeconds = timeSpent < 60 ? 60 : _currentWorklog.TimeSpentInSeconds;

            //TODO: Function to Save in a offline case
            _service.SaveWorklog(_currentWorklog);

            var totalTime = int.Parse(_associatedIssueList[_position].fields.timespent.ToString());

            _associatedIssueList[_position].fields.timespent = totalTime + _currentWorklog.TimeSpentInSeconds;
            // _myIssuesListView.Adapter = new MyIssueAdapter(this, _associatedIssueList);
            myIssueAdapter.NotifyDataSetChanged();

            _currentWorklog            = null;
            _issueTrakingTextView.Text = GetString(Resource.String.SelectIssueToTrackMessage);

            Toast.MakeText(this, GetString(Resource.String.WorklogSavedMessage), ToastLength.Short).Show();
        }
        public async Task <Response <Worklog> > DeleteWorklog([FromBody] Worklog WorklogID)

        {
            if (WorklogID == null)
            {
                return(Response <Worklog> .CreateResponse(false, "Please provide valid Worklog Id.", null));
            }


            try
            {
                var ExistingWorklog = await _worklogService.DeleteWorklog(WorklogID);


                if (ExistingWorklog == null)
                {
                    return(Response <Worklog> .CreateResponse(false, "Not a valid Worklog Id", null));
                }

                return(Response <Worklog> .CreateResponse(true, "Successfully deleted.", ExistingWorklog));
            }
            catch (Exception e)
            {
                return(Response <Worklog> .CreateResponse(false, e.Message, null));
            }
        }
Exemple #10
0
        private void StartTracking(object sender, AdapterView.ItemClickEventArgs e)
        {
            _issueTrackingTableLayout.Visibility = ViewStates.Visible;
            _position = e.Position;

            var selectedIssue = _associatedIssueList[_position];

            if (_currentWorklog != null)
            {
                if (_currentWorklog.IssueId == selectedIssue.id)
                {
                    return;
                }

                if (_currentWorklog.IssueId != selectedIssue.id)
                {
                    StopTracking();
                }
            }

            _currentWorklog         = new Worklog();
            _currentWorklog.IssueId = selectedIssue.id;
            _currentWorklog.Started = DateTime.Now;

            _timer.Start();
            _timer.Elapsed += UpdateStatus;

            Toast.MakeText(this, GetString(Resource.String.WorklogTrackingStartedMessage), ToastLength.Short).Show();
        }
Exemple #11
0
        public async Task TestLogWork()
        {
            var started = DateTimeOffset.Now.Subtract(TimeSpan.FromDays(2));

            var newWorkLog = new Worklog
            {
                TimeSpentSeconds = (long)TimeSpan.FromHours(16).TotalSeconds,
                Comment          = "Testing the logging of work",
                Started          = started
            };

            var worklog = await Client.Work.CreateAsync(TestIssueKey, newWorkLog);

            Assert.NotNull(worklog);
            Assert.Equal("2d", worklog.TimeSpent);
            Assert.NotNull(worklog.Started);
            var worklogStarted = worklog.Started.Value;

            Assert.Equal(started.AddTicks(-started.Ticks % TimeSpan.TicksPerSecond), worklogStarted.AddTicks(-worklogStarted.Ticks % TimeSpan.TicksPerSecond));
            worklog.TimeSpent        = "3d";
            worklog.TimeSpentSeconds = null;
            worklog.Started          = DateTimeOffset.Now.Subtract(TimeSpan.FromDays(3));
            await Client.Work.UpdateAsync(TestIssueKey, worklog);

            var worklogs = await Client.Work.GetAsync(TestIssueKey);

            var retrievedWorklog = worklogs.FirstOrDefault(worklogItem => string.Equals(worklog.Id, worklogItem.Id));

            Assert.NotNull(retrievedWorklog);
            Assert.Equal("3d", retrievedWorklog.TimeSpent);

            // Delete again
            await Client.Work.DeleteAsync(TestIssueKey, worklog);
        }
Exemple #12
0
        public Worklog UpdateWorklog(IssueRef issue, Worklog worklog)
        {
            try
            {
                var path    = $"issue/{issue.id}/worklog/{worklog.id}";
                var request = CreateRequest(Method.PUT, path);
                request.AddHeader("ContentType", "application/json");

                var updateData = new Dictionary <string, object>();
                if (worklog.comment != null)
                {
                    updateData.Add("comment", worklog.comment);
                }
                if (worklog.started != DateTime.MinValue)
                {
                    updateData.Add("started", worklog.started.ToString("yyyy-MM-ddTHH:mm:ss.fffzz00"));
                }
                if (worklog.timeSpentSeconds != 0)
                {
                    updateData.Add("timeSpentSeconds", worklog.timeSpentSeconds);
                }
                request.AddBody(updateData);

                var response = ExecuteRequest(request);
                AssertStatus(response, HttpStatusCode.OK);

                return(_deserializer.Deserialize <Worklog>(response));
            }
            catch (Exception ex)
            {
                Trace.TraceError("UpdateWorklog(issue, worklog) error: {0}", ex);
                throw new JiraClientException("Could not update worklog for issue", ex);
            }
        }
Exemple #13
0
        // POST api/toggl
        public async Task <string> Post([FromBody] JiraMessage jiraMessage)
        {
            var jiraUsername = ConfigurationManager.AppSettings["JiraUsername"];
            var jiraPassword = ConfigurationManager.AppSettings["JiraPassword"];

            var togglApiToken = ConfigurationManager.AppSettings["TogglAPIToken"];

            var response = "";

            if (jiraMessage.User.Name != jiraUsername)
            {
                return("");
            }

            var clientToggl = new HttpClient
            {
                BaseAddress = new Uri("https://www.toggl.com")
            };


            clientToggl.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Base64Encode($"{togglApiToken}:api_token"));

            var startDate = DateTime.UtcNow.AddDays(-1).ToString("o");
            var endDate   = DateTime.UtcNow.ToString("o");

            var responseToggl = await clientToggl.GetAsync($"api/v8/time_entries?start_date={startDate}&end_date={endDate}");

            var entries = responseToggl.Content.ReadAsAsync <IList <TogglEntry> >().Result;

            foreach (var entry in entries)
            {
                if (entry.description != jiraMessage.Issue.Key)
                {
                    continue;
                }

                var clientJira = new HttpClient
                {
                    BaseAddress = new Uri("http://jira.princiweb.net.br:7070")
                };

                clientJira.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Base64Encode($"{jiraUsername}:${jiraPassword}"));

                var started = DateTime.Now.AddSeconds(-entry.duration);

                var worklog = new Worklog
                {
                    timeSpentSeconds = entry.duration,
                    started          = FormatJIRADate(started),
                    comment          = "Teste"
                };

                var responseJira = await clientJira.PostAsJsonAsync($"rest/api/2/issue/{jiraMessage.Issue.Key}/worklog", worklog);

                response = responseJira.ToString();
            }

            return(response);
        }
 public WorkLogEntry(Worklog worklog, string jiraKey) : this()
 {
     this.Id          = worklog.id;
     this.Description = worklog.comment;
     this.Start       = worklog.started;
     this.Stop        = worklog.started.AddSeconds(worklog.timeSpentSeconds);
     this.IssueKey    = jiraKey;          //important to capture key, becuase it will be needed to update WorkLog entry in JIRA
 }
        private void WriteTimeToJira(WorkItem workItem)
        {
            var duration = workItem.Duration.RoundUp(5);
            var workLog  = new Worklog(duration.ToJiraFormat(), workItem.Started.Date, workItem.Comment);

            _jiraClient.Issues.AddWorklogAsync(workItem.TicketNumber, workLog);
            Console.WriteLine($"Work item {workItem.TicketNumber} written. {duration.ToJiraFormat()} on {workItem.Started.ToShortDateString()}");
        }
        public async Task <Worklog> DeleteWorklog(Worklog WorklogID)
        {
            _onlineTimeTrackContext.Worklogs.Remove(WorklogID);
            await _onlineTimeTrackContext.SaveChangesAsync();

            var ExistingWorklog = _onlineTimeTrackContext.Worklogs.FirstOrDefault(x => x.WorklogID == WorklogID.WorklogID);

            return(ExistingWorklog);
        }
        // POST api/toggl
        public async Task<string> Post([FromBody]JiraMessage jiraMessage)
        {
            var jiraUsername = ConfigurationManager.AppSettings["JiraUsername"];
            var jiraPassword = ConfigurationManager.AppSettings["JiraPassword"];

            var togglApiToken = ConfigurationManager.AppSettings["TogglAPIToken"];

            var response = "";

            if (jiraMessage.User.Name != jiraUsername)
                return "";

            var clientToggl = new HttpClient
            {
                BaseAddress = new Uri("https://www.toggl.com")
            };


            clientToggl.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Base64Encode($"{togglApiToken}:api_token"));

            var startDate = DateTime.UtcNow.AddDays(-1).ToString("o");
            var endDate = DateTime.UtcNow.ToString("o");

            var responseToggl = await clientToggl.GetAsync($"api/v8/time_entries?start_date={startDate}&end_date={endDate}");

            var entries = responseToggl.Content.ReadAsAsync<IList<TogglEntry>>().Result;

            foreach (var entry in entries)
            {
                if (entry.description != jiraMessage.Issue.Key)
                    continue;

                var clientJira = new HttpClient
                {
                    BaseAddress = new Uri("http://jira.princiweb.net.br:7070")
                };

                clientJira.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Base64Encode($"{jiraUsername}:${jiraPassword}"));

                var started = DateTime.Now.AddSeconds(-entry.duration);

                var worklog = new Worklog
                {
                    timeSpentSeconds = entry.duration,
                    started = FormatJIRADate(started),
                    comment = "Teste"
                };

                var responseJira = await clientJira.PostAsJsonAsync($"rest/api/2/issue/{jiraMessage.Issue.Key}/worklog", worklog);

                response = responseJira.ToString();
            }

            return response;
        }
 public void AddWorklog(Worklog worklog)
 {
     using (SQLiteCommand command = _connection.CreateCommand())
     {
         command.CommandText = "INSERT INTO Worklog (IssueKey, Start, DurationInSeconds) VALUES (@IssueKey, @Start, @DurationInSeconds)";
         command.Parameters.AddWithValue("@IssueKey", worklog.Issue.Key);
         command.Parameters.AddWithValue("@Start", worklog.Start.Ticks);
         command.Parameters.AddWithValue("@DurationInSeconds", worklog.DurationInSeconds);
         command.ExecuteNonQuery();
     }
 }
Exemple #19
0
 public void Save(Worklog worklog)
 {
     if (worklog.ID == 0)
     {
         Insert(worklog);
     }
     else
     {
         Update(worklog);
     }
 }
Exemple #20
0
 public static WorklogModel ToModel(Worklog domain)
 {
     return(new WorklogModel
     {
         Date = domain.Date,
         Hours = domain.Hours,
         User = domain.User,
         IssueKey = domain.IssueKey,
         ProjectKey = domain.ProjectKey,
         Category = domain.Category
     });
 }
Exemple #21
0
        private void toolStripButtonEdit_Click(object sender, EventArgs e)
        {
            Worklog             worklog        = (Worklog)bindingSourceWorklogs.Current;
            FormAddEditWorklogs formAddWorklog = new FormAddEditWorklogs(worklog);

            if (formAddWorklog.ShowDialog() == DialogResult.OK)
            {
                WorklogsRepository worklogsRepository = new WorklogsRepository();
                worklogsRepository.Save(worklog);

                BindControls();
            }
        }
        public bool SaveWorklog(Worklog worklog)
        {
            var apiUrl = string.Format(_baseUrl.Replace("#DOMAIN#", _domain) + _worklogQry, worklog.IssueId);

            var item = new
            {
                comment          = "My TimeTracker Input",
                started          = worklog.Started.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzz00"),
                timeSpentSeconds = worklog.TimeSpentInSeconds
            };

            return(_apiService.PostItem(apiUrl, item).Result);
        }
        public async Task <Worklog> Worklog(Worklog worklog)
        {
            // save the worklog
            var Worklog = await _onlineTimeTrackContext.Worklogs.AddAsync(worklog);

            worklog.Feature      = worklog.Feature;
            worklog.DateAdded    = DateTime.UtcNow;
            worklog.DateModified = DateTime.UtcNow;
            await _onlineTimeTrackContext.SaveChangesAsync();

            // return the worklog
            return(worklog);
        }
        public void DeleteWorklog(Worklog worklog)
        {
            if (!worklog.Id.HasValue)
            {
                throw new InvalidOperationException();
            }

            using (SQLiteCommand command = _connection.CreateCommand())
            {
                command.CommandText = "DELETE FROM Worklog WHERE Id = @Id";
                command.Parameters.AddWithValue("@Id", worklog.Id.Value);
                command.ExecuteNonQuery();
            }
        }
Exemple #25
0
 private void OnServerUpdate(string elapsedTimeFormatted)
 {
     Console.WriteLine("elapsedTimeFormatted: " + elapsedTimeFormatted);
     if (activeWorklog == null)
     {
         activeWorklog = programManagerProvider.GetProgramManager()
                         .LogTime(selectedIssue.key, elapsedTimeFormatted);
     }
     else
     {
         var updateSuccess = programManagerProvider.GetProgramManager()
                             .UpdateLogTime(selectedIssue.key, elapsedTimeFormatted, activeWorklog.id);
     }
 }
        private static Worklog GetWorklogFromTxtLine(string line)
        {
            string[] numberLineSeparatedItems = line.Split(' ');
            line = line.Substring(numberLineSeparatedItems[0].Length + 1, line.Length - 1 - numberLineSeparatedItems[0].Length);

            string[] worklogData = line.Split(new string[] { " - " }, StringSplitOptions.None);
            Worklog  worklog     = new Worklog();

            worklog.key = worklogData[0];
            worklog.timeSpentSeconds = worklogData[1];
            worklog.comment          = worklogData[2];
            worklog.dateStarted      = string.Format("{0}{1:zz}00", DateTime.Now, DateTime.Now);

            return(worklog);
        }
Exemple #27
0
        public void DeleteWorklog(IssueRef issue, Worklog worklog)
        {
            try
            {
                var path    = $"issue/{issue.id}/worklog/{worklog.id}";
                var request = CreateRequest(Method.DELETE, path);

                var response = ExecuteRequest(request);
                AssertStatus(response, HttpStatusCode.NoContent);
            }
            catch (Exception ex)
            {
                Trace.TraceError("DeleteWorklog(issue, worklog) error: {0}", ex);
                throw new JiraClientException("Could not delete worklog", ex);
            }
        }
Exemple #28
0
        private void toolStripButtonDelete_Click(object sender, EventArgs e)
        {
            Worklog      worklog = (Worklog)bindingSourceWorklogs.Current;
            DialogResult result  = MessageBox.Show("Are you sure you want to delete this user?",
                                                   "Confirmation",
                                                   MessageBoxButtons.YesNo,
                                                   MessageBoxIcon.Question,
                                                   MessageBoxDefaultButton.Button2);

            if (result == DialogResult.Yes)
            {
                WorklogsRepository worklogsRepository = new WorklogsRepository();
                worklogsRepository.Delete(worklog);
                BindControls();
            }
        }
Exemple #29
0
 public ActionResult Delete(Worklog worklog, int projectId, int taskId, int id)
 {
     try
     {
         if (User.Identity.IsAuthenticated)
         {
             IWorklogLogic logic = container.Resolve <IWorklogLogic>();
             logic.HandleWorklogDelete(projectId, taskId, id, User.Identity.Name);
             return(Json(new JsonDataHandler(httpCode: HttpCodeEnum.OK, message: "Worklog successfully removed!").getInfo(), JsonRequestBehavior.AllowGet));
         }
         return(Json(new JsonDataHandler(httpCode: HttpCodeEnum.Forbidden).getWarning(), JsonRequestBehavior.AllowGet));
     }
     catch (Exception ex)
     {
         return(Json(new JsonDataHandler(ex).getError(), JsonRequestBehavior.AllowGet));
     }
 }
Exemple #30
0
        public async Task LogWork(Worklog worklog)
        {
            if (worklog.IsAtLeast(TimeSpan.FromMinutes(1)))
            {
                RestRequest restRequest = CreateRequest("issue/" + worklog.Issue.Key + "/worklog", Method.POST);
                var         worklogJson = new
                {
                    started          = worklog.Start,
                    timeSpentSeconds = worklog.DurationInSeconds
                };
                restRequest.AddJsonBody(worklogJson);

                IRestResponse response = await _restClient.ExecuteTaskAsync(restRequest);

                response.EnsureSuccessStatusCode();
            }
        }
        public void Update(Worklog worklog, string Feature = null)
        {
            var Worklog = _onlineTimeTrackContext.Projects.Find(worklog.WorklogID);

            if (worklog == null)
            {
                throw new AppException("worklog not found");
            }

            if (worklog.Feature != worklog.Feature)
            {
                //Features has changed so check if the new Feature is already taken
                if (_onlineTimeTrackContext.Worklogs.Any(x => x.Feature == worklog.Feature))
                {
                    throw new AppException("Worklog " + worklog.Feature + " is already taken");
                }
            }
        }
        public async Task <Response <Worklog> > Worklog([FromBody] Worklog worklog)
        {
            if (worklog == null)
            {
                return(Response <Worklog> .CreateResponse(false, "Please provide valid Worklog Id.", null));
            }

            try
            {
                var newWorklog = await _worklogService.Worklog(worklog);

                return(Response <Worklog> .CreateResponse(true, "Successfully uploaded.", newWorklog));
            }
            catch (Exception e)
            {
                return(Response <Worklog> .CreateResponse(false, e.Message, null));
            }
        }
        public void LogTime(Issue jiraIssue, DateTime exportTimeStamp, TimeSpan exportTime, WorklogStrategy strategy, string comment = "", TimeSpan? remainingTime = null)
        {
            var wasClosed = TryReopenJira(jiraIssue);

            if (string.IsNullOrWhiteSpace(comment)) comment = "No Comment Entered";
            comment = "Gallifrey: " + comment;

            var worklog = new Worklog(string.Format("{0}h {1}m", exportTime.Hours, exportTime.Minutes), DateTime.SpecifyKind(exportTimeStamp, DateTimeKind.Local), comment);
            string remaining = null;
            if (remainingTime.HasValue)
            {
                remaining = string.Format("{0}h {1}m", exportTime.Hours, exportTime.Minutes);
            }
            try
            {
                jiraIssue.AddWorklog(worklog, strategy, remaining);
            }
            catch (Exception ex)
            {
                throw new WorkLogException("Error logging work", ex);
            }

            if (wasClosed)
            {
                try
                {
                    ReCloseJira(jiraIssue);
                }
                catch (Exception ex)
                {
                    throw new StateChangedException("Time Logged, but state is now open", ex);
                }
            }
        }