Beispiel #1
0
        public bool HasMatchingActivityTimestamps(Stamp stamp, out string error)
        {
            var  stampTime    = DayTime(stamp);
            var  activityTime = TimeSpan.FromMinutes(stamp.ActivityRecords.Sum(r => Total(r).TotalMinutes));
            bool isMatching   = stampTime == activityTime;

            error = isMatching ? null : $"The sum value of the day stamps ({stampTime}) does not match with the sum value of the activities ({activityTime}).";
            return(isMatching);
        }
Beispiel #2
0
        public static void SetActivityBegin(Stamp stamp, ActivityRecord activity, TimeSpan value)
        {
            // invalid action:
            if (value >= activity.End)
            {
                return;
            }

            // add new, pending activity entry if applicable:
            if (!stamp.ActivityRecords.Contains(activity))
            {
                stamp.ActivityRecords.Add(activity);
            }

            var ordered = stamp.ActivityRecords.OrderBy(r => r.Begin).ToList();

            // first activity start changed -> also change days start stamp
            if (activity.Begin == stamp.Begin)
            {
                stamp.Begin = value;
            }
            // in between start changed -> also change end of previous activity, if they previously matched
            else if (activity != ordered.FirstOrDefault())
            {
                int index = ordered.IndexOf(activity) - 1;

                bool isIterating;
                do
                {
                    isIterating = false;
                    var previousActivity = ordered.ElementAt(index); //grdActivities.Rows[index].Tag as ActivityRecord;
                    if (!previousActivity.End.HasValue || previousActivity.End.Value >= value)
                    {
                        previousActivity.End = value;
                        // activity is hidden / negative after change -> remove activity
                        if (Total(previousActivity) < TimeSpan.Zero)
                        {
                            stamp.ActivityRecords.Remove(previousActivity);
                            if (index == 0)
                            {
                                // removed first stamp -> also set stamp begin
                                stamp.Begin = value;
                            }
                            index--;
                            isIterating = true;
                        }
                    }
                } while (index >= 0 && isIterating);
            }

            activity.Begin = value;

            // pause interruption gap(s) is/are changed -> also change day pause stamp
            CalculatePauseFromActivities(stamp);
        }
Beispiel #3
0
 public TimeSpan DayTime(Stamp stamp)
 {
     if (stamp.Day == Time.Today && stamp.End == TimeSpan.Zero)
     {
         return(GetNowTime().Subtract(stamp.Begin).Subtract(stamp.Pause));
     }
     else
     {
         return(stamp.End.Subtract(stamp.Begin).Subtract(stamp.Pause));
     }
 }
Beispiel #4
0
        public bool DeleteStamp(Stamp stamp)
        {
            if (stamp != null)
            {
                int index = StampList.IndexOf(stamp);
                if (index != -1)
                {
                    StampList.Remove(stamp);
                    CurrentShown = StampList.Count > index?StampList.ElementAt(index) : StampList.ElementAt(index - 1);

                    return(true);
                }
            }
            return(false);
        }
Beispiel #5
0
        public static void SetActivityEnd(Stamp stamp, ActivityRecord activity, TimeSpan value)
        {
            // invalid action:
            if (value <= activity.Begin)
            {
                return;
            }

            var ordered = stamp.ActivityRecords.OrderBy(r => r.Begin).ToList();

            // last activity end changed -> also change days end stamp
            if (activity.End == stamp.End)
            {
                stamp.End = value;
            }
            // in between end changed -> also change start of next activity, if they previously matched
            else if (activity != ordered.LastOrDefault())
            {
                int index = ordered.IndexOf(activity) + 1;

                bool isIterating;
                do
                {
                    isIterating = false;
                    var nextActivity = ordered.ElementAt(index);
                    if (nextActivity.Begin <= value)
                    {
                        nextActivity.Begin = value;
                        // activity is hidden / negative after change -> remove activity
                        if (Total(nextActivity) < TimeSpan.Zero)
                        {
                            stamp.ActivityRecords.Remove(nextActivity);
                            if (index == ordered.Count - 1)
                            {
                                // removed last stamp -> also set stamp end
                                stamp.End = value;
                            }
                            index++;
                            isIterating = true;
                        }
                    }
                } while (index <= ordered.Count - 1 && isIterating);
            }
            activity.End = value;

            // pause interruption gap(s) is/are changed -> also change day pause stamp
            CalculatePauseFromActivities(stamp);
        }
Beispiel #6
0
        private double?GetTimeForExcelCell(Stamp stamp, string activity = null)
        {
            IEnumerable <ActivityRecord> activities = stamp.ActivityRecords;

            if (activity != null)
            {
                activities = activities.Where(r => r.Activity == activity);
            }

            var span = TimeSpan.FromMinutes(activities.Sum(r => TimeManager.Total(r).TotalMinutes));

            if (span == TimeSpan.Zero)
            {
                return(null); // String.Empty;
            }
            return(span.RoundToTotalQuarterHours());
            //return span.ToString("hh\\:mm");
        }
Beispiel #7
0
        private List <Stamp> LoadStampListXml(XElement xml)
        {
            if (Settings.IgnoreStampFile)
            {
                return(new List <Stamp>());
            }

            List <Stamp> stamps = new List <Stamp>();

            foreach (var stampXml in xml.Elements("Stamp"))
            {
                var stamp = new Stamp(Settings.DefaultWorkingHours)
                {
                    Day          = Convert.ToDateTime(stampXml.Element("day").Value),
                    Begin        = ParseHHMM(stampXml.Element("begin").Value),
                    End          = ParseHHMM(stampXml.Element("end").Value),
                    Pause        = SerializeMM(stampXml.Element("pause").Value),
                    Comment      = stampXml.Element("comment") != null?stampXml.Element("comment").Value              : String.Empty,
                    WorkingHours = stampXml.Element("hours") != null?Convert.ToInt32(stampXml.Element("hours").Value) : 8   // legacy, when value was not configurable it was always 8
                };

                if (stampXml.Element("Activities") != null)
                {
                    stamp.ActivityRecords.Clear();
                    foreach (var actxml in stampXml.Element("Activities").Elements("Activity"))
                    {
                        stamp.ActivityRecords.Add(new ActivityRecord()
                        {
                            Activity = actxml.Attribute("Name").Value,
                            Begin    = ParseHHMM(actxml.Attribute("Begin").Value),
                            End      = ParseHHMM(actxml.Attribute("End").Value),
                            Comment  = actxml.Attribute("Comment").Value
                        });
                    }
                }

                stamps.Add(stamp);
            }
            return(stamps);
        }
Beispiel #8
0
 public void DeleteActivity(Stamp stamp, ActivityRecord activity)
 {
     if (activity == TodayCurrentActivity)
     {
         // todays end auf neuen letzten zeitpunkt setzen, current activity clearen:
         stamp.ActivityRecords.Remove(activity);
         TodayCurrentActivity = null;
         var newLastActivity = stamp.GetLastActivity();
         stamp.End = newLastActivity.End.Value;
         // TODO: need to recalculate pause?
         TimeManager.CalculatePauseFromActivities(stamp);
     }
     else if (activity == stamp.GetFirstActivity())
     {
         // update todays start time:
         stamp.ActivityRecords.Remove(activity);
         var newFirstActivity = stamp.GetFirstActivity();
         stamp.Begin = newFirstActivity.Begin.Value;
         // TODO: need to recalculate pause?
         TimeManager.CalculatePauseFromActivities(stamp);
     }
     else if (activity == stamp.GetLastActivity())
     {
         // update todays end time:
         stamp.ActivityRecords.Remove(activity);
         var newLastActivity = stamp.GetLastActivity();
         stamp.End = newLastActivity.End.Value;
         // TODO: need to recalculate pause?
         TimeManager.CalculatePauseFromActivities(stamp);
     }
     else
     {
         // in between activity; update todays pause time:
         stamp.ActivityRecords.Remove(activity);
         TimeManager.CalculatePauseFromActivities(stamp);
     }
 }
Beispiel #9
0
        public static PopupDialog ShowCurrentlyTrackingActivityOnNewDay(string activity, Stamp previousDay)
        {
            string greeting;

            if (TimeManager.Time.Now.Hour < 11)
            {
                greeting = "Good morning";
            }
            else if (TimeManager.Time.Now.Hour < 13)
            {
                greeting = "Good noon";
            }
            else if (TimeManager.Time.Now.Hour < 18)
            {
                greeting = "Good afternoon";
            }
            else
            {
                greeting = "Good evening";
            }

            string message;

            if (previousDay == null)
            {
                message = $"{greeting}! You are continuing with {activity}...";
            }
            else
            {
                string previous;
                if (previousDay.Day == TimeManager.Time.Today.Subtract(TimeSpan.FromDays(1)))
                {
                    previous = "Yesterday";
                }
                else if (previousDay.Day > TimeManager.Time.Today.Subtract(TimeSpan.FromDays(7)))
                {
                    previous = $"Last {previousDay.Day.DayOfWeek.ToString()}";
                }
                else
                {
                    previous = "Last day";
                }

                message = $"{greeting}! {previous}'s working time was {Manager.FormatTimeSpan(Manager.DayTime(previousDay))}.{Environment.NewLine}You are continuing with {activity}...";
            }

            var diag = new PopupDialog(message, null, TimeSpan.FromSeconds(8));

            diag.StartShowing(DefaultOwner);
            return(diag);
        }
Beispiel #10
0
        public void SetPause(Stamp stamp, TimeSpan value)
        {
            if (stamp.ActivityRecords.Any())
            {
                // pause is only a number in minutes, there is not necessarily a 'start' / 'end' time available.
                // if pause has been recognized automatically, the function will cut off the activity before the pause and continues it after the pause, resulting in an activity 'gap', which indeed would be the pause start/end times.

                // find activity gap, created by automatic pause recognition function:
                // get the latest activity before pause, that is, the activity with an end time and no other activity with a matching start time (leaving the gap afterwards):
                // this may also be the last activity of the day, if the day has been finished, and no other pause gap has been found
                var activityBeforePause = stamp.ActivityRecords.FirstOrDefault(a => a.End.HasValue && !stamp.ActivityRecords.Any(aa => Math.Round(aa.Begin.Value.TotalMinutes) == Math.Round(a.End.Value.TotalMinutes)));

                TimeSpan timeDiff = (value - stamp.Pause);

                if (activityBeforePause != null)
                {
                    bool canDistribute = true;
                    do
                    {
                        canDistribute           = true;
                        activityBeforePause.End = activityBeforePause.End.Value - timeDiff;
                        // should not result in negative activity time -> remove completely and apply change to previous activity
                        if (Total(activityBeforePause) < TimeSpan.Zero)
                        {
                            timeDiff = TimeSpan.Zero - Total(activityBeforePause);
                            var previousActivity = stamp.ActivityRecords.FirstOrDefault(a => a.End.HasValue && a.End.Value == activityBeforePause.Begin.Value);
                            if (previousActivity != null) // can this ever be null? (except for having an unreasonable long break of 4 hours...)
                            {
                                stamp.ActivityRecords.Remove(activityBeforePause);
                                activityBeforePause = previousActivity;
                                canDistribute       = false;
                            }
                        }
                    } while (!canDistribute);
                }
                else
                {
                    // however, this may also be null, if the day is not yet finished (last is open-end) and the pause has not been recognized. in this case, just modify the first.
                    var starting = stamp.ActivityRecords.FirstOrDefault();

                    bool canDistribute = true;
                    do
                    {
                        canDistribute  = true;
                        starting.Begin = starting.Begin.Value + timeDiff; // should expand in the other direction

                        // should not result in negative activity time -> remove completely and apply change to next activity
                        if (starting.End.HasValue && Total(starting) < TimeSpan.Zero)
                        {
                            timeDiff = TimeSpan.Zero - Total(starting);
                            var nextActivity = stamp.ActivityRecords.FirstOrDefault(a => a.Begin.Value == starting.End.Value);
                            if (nextActivity != null) // can this ever be null?
                            {
                                stamp.ActivityRecords.Remove(starting);
                                starting      = nextActivity;
                                canDistribute = false;
                            }
                        }
                    } while (!canDistribute);
                }
            }
            stamp.Pause = value;
        }
Beispiel #11
0
 public TimeSpan DayBalance(Stamp stamp) => DayTime(stamp) - TimeSpan.FromHours(stamp.WorkingHours);
Beispiel #12
0
        private bool RestoreLastActivity(Stamp today)
        {
            // assuming the last activity is still valid, and the downtime is not considered a break:
            // if there is no running activity, try to restore the last activity
            if (TodayCurrentActivity == null)
            {
                var openEnd = today.ActivityRecords.FirstOrDefault(r => !r.End.HasValue);
                if (openEnd != null) // this case should actually never happen?
                {
                    Log.Add("Warning: RestoreLastActivity has found an open end activity. " + new StackTrace().ToString());
                    TodayCurrentActivity = openEnd;
                }
                else
                {
                    // this branch should be the default case for this method:

                    // get lastest logged activity:
                    var last = today.GetLastActivity();

                    // the downtime is considered a break:
                    if (IsQualifiedPauseBreak)
                    {
                        // determine pause time from last qualified event:

                        TimeSpan pauseStartTime;

                        // last lock off time is 'master':
                        if (Settings.IsLockingComputerWhenLeaving)
                        {
                            pauseStartTime       = last.End.Value;
                            TodayCurrentActivity = last;
                        }
                        // last mouse movement time is 'master':
                        else
                        {
                            pauseStartTime = GetTime(LastMouseMove.TimeOfDay); // should be on same day... ;-)
                            LastMouseMove  = default(DateTime);

                            last.End             = pauseStartTime;
                            TodayCurrentActivity = last;
                        }

                        // set pause:
                        Today.Pause = GetNowTime() - pauseStartTime;

                        // ... and resume with a new activity now:
                        TodayCurrentActivity = null;
                        StartNewActivity(last.Activity, last.Comment);
                        return(true);
                    }
                    // the downtime is not considered a break:
                    // otherwise, if log off time since then was more than 7 minutes, create and start new activity record (better documented, as the end time of the 'relative longer' absence is not lost):
                    else if (GetNowTime() - last.End.Value > TimeSpan.FromMinutes(7))
                    {
                        today.ActivityRecords.Add(new ActivityRecord()
                        {
                            Activity = last.Activity, Begin = last.End.Value, End = GetNowTime()
                        });
                        StartNewActivity(last.Activity, null);
                    }
                    // otherwise, assume this is a total unrelevant break, e.g. fetched a cup of coffee, went to toilet, so just resume that activity (end time is reset to null, ergo lost and not documented):
                    else
                    {
                        TodayCurrentActivity = last;
                        last.End             = null;
                    }
                }
            }
            return(false);
        }
Beispiel #13
0
        public void ResumeStamping()
        {
            // assuming here that activity is null!
            //if (TodayCurrentActivity != null)
            //throw new NotSupportedException($"TodayCurrentActivity is not null after resume: {TodayCurrentActivity.Activity}, started: {TodayCurrentActivity.Begin}");

            // this can happen when:
            // starting the app (e.g. new day autostart, same day computer restart)
            // resuming from sleep (e.g. new day resume, same day resume)
            // resuming from lunch break (insert pause parameter)..

            // find existing stamp for today:

            IsStamping = true;

            var existing = StampList.SingleOrDefault(t => t.Day == Time.Today);

            if (existing != null)
            {
                Today     = CurrentShown = existing;
                Today.End = TimeSpan.Zero;

                // TODO: (optionally) ask in a notification, whether have been working or not since last known stamp (default yes, if choosing no will automatically insert a pause)...

                bool hasSetPause = RestoreLastActivity(Today);

                // update event (for ui):
                CurrentActivityUpdated();

                // show notification:
                if (hasSetPause)
                {
                    //new Task(() =>
                    //{
                    //    System.Threading.Thread.Sleep(10000);
                    //}).Start();
                    PopupDialog.ShowAfterPause(Today.Pause, TodayCurrentActivity.Activity);
                }
                else
                {
                    PopupDialog.ShowCurrentlyTrackingActivity(TodayCurrentActivity.Activity);
                }

                return;
            }

            // new day, new stamp:

            TodayCurrentActivity = null;
            Today = CurrentShown = new Stamp(Time.Today, GetNowTime(), Settings.DefaultWorkingHours);
            StampList.Add(Today);

            // not specified ? -> keep tracking for latest activity...
            if (String.IsNullOrEmpty(Settings.AlwaysStartNewDayWithActivity))
            {
                foreach (var day in StampList.OrderByDescending(s => s.Day))
                {
                    if (!day.ActivityRecords.Any())
                    {
                        continue;
                    }
                    StartNewActivity(day.GetLastActivity().Activity, null);
                    break;
                }
            }

            // if not yet set -> start tracking either default activity or just the first activity...
            if (TodayCurrentActivity == null)
            {
                StartNewActivity(Settings.AlwaysStartNewDayWithActivity ?? Settings.TrackedActivities.ElementAt(0), null);
            }

            var previous = StampList.Where(s => s.Day < Today.Day).OrderByDescending(s => s.Day).FirstOrDefault();

            PopupDialog.ShowCurrentlyTrackingActivityOnNewDay(TodayCurrentActivity.Activity, previous);
        }