Determine time blocks from multiple concurrent time stamps in PHP - php

We have a time tracking system where we start a timer for each project we are working on, then stop when complete. There are many times that we have multiple timers going in the same block of time. for example:
Given three timers:
Start Time | End Time | Time Spent
10/23/2012 10:15:00 AM | 10/23/2012 10:45:00 AM | 30
10/23/2012 10:15:00 AM | 10/23/2012 11:15:00 AM | 60
10/23/2012 11:05:00 AM | 10/23/2012 11:35:00 AM | 30
Both are running at the same time, and if used with our time reporting would amount to 2 hours being counted, even though the times are inside of each other when the valid amount of time spent is 1 hour and 20 minutes.
So I am trying to figure out how to go through the time entries and only count the valid time periods.
Any ideas how to calculate this in PHP?

Related

Executing tasks in various intervals

I have table in my mysql as follows :
+----+---------------+--------------+----------------------+---------------------+
| id | report_name | report_id | rinterval | last_run |
+----+---------------+--------------+----------------------+---------------------+
| 1 | test report 1 | 434234234234 | every morning | 2016-05-20 12:55:07 |
| 2 | test report 2 | 3434232 | every sunday morning | 2016-05-20 12:55:07 |
| 3 | test report 3 | 342423423 | never | 2016-05-20 12:55:07 |
| 4 | test report 4 | 4324234 | every morning | 2016-05-20 12:55:07 |
+----+---------------+--------------+----------------------+---------------------+
I am trying to create a php script (preferably) that when is called, runs the appropriate report. I would like some suggestions on the best way to do that.
Let's assume I set a cron job to call the script every morning, and the intervals are as above (+similar): (every morning, every sunday, twice a month, etc). Also let's assume that the scripts should not run automatically if less than 24 hours have passed. Also a manual call can be initiated.
I was thinking something like this :
Call script
Find what day and time it is
Find what day it is
Select * from above where day and time more than 24 hours
Iterate the above records and run the reports (report is run like http://example.com/report_name/report_id)
If rinterval reads "every morning" - run the report
If rinterval reads "every Sunday morning" - run the report if it is Sunday (and similar for other days using a case)
If rinterval reads "never" - do not run the report
if rinterval reads "twice a month" - find the last day run and see if the interval is more than 15 - if yes run it. (or similar)
In all the above cases, on succesfull run, update the last_run timestamp.
One of my problems is what happens if I run a manual call - or if I want to run 2 manual calls 2 minutes apart for testing. If I run the report say on Monday afternoon, I still want it to be run on Tuesday morning. Should I introduce another column that indicates if this is manual call or automatic? I still need to know that the run was made if it is manual, but do not want to break the schedule as the report must be run before 08.00 in the morning.
What are your thoughts? I am sure there is a more efficient way to do this. I am open to all suggestions, I am doing this from scratch.
One of my problems is what happens if I run a manual call - or if I want to run 2 manual calls 2 minutes apart for testing
That's depends on what your scripts are doing.
I still need to know that the
Then simply keep log of invocations (separate table) with i.e. name of script, date of invocation, way of invocation.

Complex MySQL Query - Checking for overlapping DATE intervals

I'm trying to write a function to move an scheduled task. The schedule can not overlap with any other event. My user inputs are as follows:
schedule_id (int)
new_start_time (DATETIME)
My table structure is as follows:
Schedules
| schedule_id | start_time | end_time | task_id
| 1 | 2015-12-21 02:00:00 | 2015-12-21 04:00:00 | 1
| 2 | 2015-12-21 08:30:00 | 2015-12-21 09:30:00 | 1
| 3 | 2015-12-22 01:00:00 | 2015-12-22 02:00:00 | 2
Tasks
| task_id | name | max_duration
| 1 | do things | 2
| 2 | do stuff | 1
A user has between start_time and end_time to start a "task". The user can not begin the "task" until that window. Once that user begins the task they have whatever the max_duration for that task ID is to complete it. There is also a 15 minute window to set up for the next task. That means a user who starts a task 1 second before the end of the window still has max_duration amount of time to complete the task. Therefore the "actual window" that nothing can be scheduled in is start_time to (end_time+max_duration+15). I would like to move an event (or insert a new one) but I must check for overlaps. Essentially I must ensure:
Does the start_time from user input run into any other schedule's end_time+max_duration+15?
Does the end_time+max_duration+15 run into any other schedule's start time. end_time is simply obtained by taking the new start_time and adding the original duration (end_time = (orig_end_time-orig_start_time)+start_time
For example, the above table is valid for schedule_id's 1 and 2 because a user can start any time between 2:00 and 4:00. Assuming he starts right at the end, 3:59:59 the event will last at max until 5:59:59. Even with the cleanup window of 15 minutes this still leads to 6:14:59 and since the next schedule starts at 8:30 this is ok.
I've been wrapping my head around this for hours. I would like to do it in pure MySQL however I am considering using PHP if I really have to. Even in PHP this problem seems difficult. Sure I could grab every schedule with a start time a day or two earlier and an end time a day or two later then compare my interval but that seems very hacky.
Any ideas?

Calculating a Running Total Efficiently

I'm having trouble figuring out how to store, retrieve and calculate running balances in PHP. It is not related to PHP - it is more programming theory, but anyway:
Imagine the following table:
id | user_id | date | start_time | end_time | total | balance | notes
19 | 17 | 2014-04-01 | 09:00:00 | 17:30:00 | 7.50 | 0 |
20 | 18 | 2014-04-02 | 09:00:00 | 18:30:00 | 8 | 0.5 |
20 | 18 | 2014-04-01 | 09:00:00 | 17:00:00 | 7 | -0.5 |
Displaying the balance for the month is relatively easy:
Figure out how many hours are available in the month (April 2014). (Let's assume 7.5 working hours per day and we don't work weekends):
22 Days * 7.5 = 165 Hours
Count the total time booked for a month using SQL, eg the following:
SELECT SUM(total) FROM booking WHERE date >= '2014-04-01' AND date <= '2014-04-01';
//30
Balance then is total hours worked minus total available hours:
30 - 165 = -135
I need to display a running total of these values in an efficient way across multiple months, therefore I didn't just want to SUM() the total column as there could be thousands of rows in the future, surely this is in-efficient?
Therefore I thought about having a running_totals table, with a row per-user, per-month which would auto-update whenever a record is edited, added or removed. However, this would be problematic due to the fact the records in previous months can be updated, it would not only require updating the current month row, it would have to cascade to the months in front of that month. For example if you were to go back to March to update a record, I would have to update the running_balance row for March and April.
Unless you SUM all records in running_balance, and work out the balance in PHP? Again this seems inefficient to me as the application grows, I would rather get it right at the beginning.
Other Problems
I only want to calculate the running balance up until today, however you are allowed to book time in the future, this should be ignored when calculating the running balance.
How I currently do it
I imagine hopefully pretty fast, but I think it's too complicated and there has already been bugs in it. So I need to get a simple, efficient solution in quick.
Inserting a new record
First time you book a day in a new month, I work out the total hours available in that month( EG April 2014)
165
Load the running_balance row (1 per user) and minus the month total from it:
0 - 165 = -165
Add the currently booked total on (Eg 7.5):
-165 + 7.5 = -157.5
Displaying the running balance
Load the row from running_balance. Calculate how many hours are remaining in the current month. Eg we are on 28th April so there are 15 hours remaining (2 Days * 7.5).
Add that to the running balance:
-157.5 + 15 = -142.5
Your running balance is therefore: -142.5
This is just proving to be too complicated with lots of edge cases especially when editing & removing. In particular when removing the last entry for a particular month. So, I am wondering if there is a simpler approach, you clever folk can think of?
Thanks.
PS: The source for the code is here: https://github.com/WeareJH/Flexitime
It's not really in any state to be used by the public before issues have been ruled out and docs written, but it may help with context.
A "materialized view" can store aggregated values which are maintained by the database, resulting in fast read performance at the expense of write performance.
However, "MySQL doesn't support materialized views natively, but workarounds can be implemented by using triggers or stored procedures or by using the open-source application Flexviews."
See also other answers discussing stored aggregates and normalization in general:
Storing vs calculating aggregate values
Does storing aggregated data go against database normalization?
When is it useful to store aggregated data in SQL [closed]
How do you determine how far to normalize a database?
For other background, see also:
Denormalization Patterns

setting event to happen inbetween certain time

so i have a database that have a set of events that are suppose to happen and end at certain time. So say my table is (the event here is an actual event, not a code thing)
Event | TimeStart | TimeEnd | Day
A | 0800 | 1400 | 2
B | 2000 | 2300 | 3
C | 1200 | 1900 | 4
What i want is that i want the event to occur IF its between the TimeEnd and TimeEnd + 3 hours. The problem that i encounter is what if its 2300? I used the days with date(N) that correspond to monday (1) - Sunday (7)
So if its Event B, i need to have a code that take the current time, reduce it by 3 hours (so that i doesn't go to day 4) then i get the TimeEnd and add on 3 hours to it.
The problem now that i tested is that How do i make Event B to be on Day 3? even if i used strtotime() Event B still shows day 4 after it passed 2400 hour.
To make the matter clearer, i am creating a voting poll that only starts after the event ended and it only last for 3 hours.
Edit found the answer, didn't know using "Monday this week" made sense for date() :D
Your representation of times in the table is kinda weird. If each event will happen only one time (doesn't repeat for instance every monday), I suggest you use unix timestamp to represent the times. Then you will have no such problems.

How to effectively execute this cron job?

I have a table with 200 rows. I'm running a cron job every 10 minutes to perform some kind of insert/update operation on the table. The operation needs to be performed only on 5 rows at a time every time the cron job runs. So in first 10 mins records 1-5 are updated, records 5-10 in the 20th minute and so on.
When the cron job runs for the 20th time, all the records in the table would have been updated exactly once. This is what is to be achieved at least. And the next cron job should repeat the process again.
The problem:
is that, every time a cron job runs, the insert/update operation should be performed on N rows (not just 5 rows). So, if N is 100, all records would've been updated by just 2 cron jobs. And the next cron job would repeat the process again.
Here's an example:
This is the table I currently have (200 records). Every time a cron job executes, it needs to pick N records (which I set as a variable in PHP) and update the time_md5 field with the current time's MD5 value.
+---------+-------------------------------------+
| id | time_md5 |
+---------+-------------------------------------+
| 10 | 971324428e62dd6832a2778582559977 |
| 72 | 1bd58291594543a8cc239d99843a846c |
| 3 | 9300278bc5f114a290f6ed917ee93736 |
| 40 | 915bf1c5a1f13404add6612ec452e644 |
| 599 | 799671e31d5350ff405c8016a38c74eb |
| 56 | 56302bb119f1d03db3c9093caf98c735 |
| 798 | 47889aa559636b5512436776afd6ba56 |
| 8 | 85fdc72d3b51f0b8b356eceac710df14 |
| .. | ....... |
| .. | ....... |
| .. | ....... |
| .. | ....... |
| 340 | 9217eab5adcc47b365b2e00bbdcc011a | <-- 200th record
+---------+-------------------------------------+
So, the first record(id 10) should not be updated more than once, till all 200 records are updated once - the process should start over once all the records are updated once.
I have some idea on how this could be achieved, but I'm sure there are more efficient ways of doing it.
Any suggestions?
You could use a Red/Black system (like for cluster management).
Basically, all your rows start out as black. When you run your cron, it will mark the rows it updated as "Red". Once all the rows are red, you switch, and now start turning all the red rows to be black. You keep this alternation going, and it should allow you to effectively mark rows so that you do not update them twice. (You could store whatever color goal you want in a file or something so that it is shared between crons)
I would just run the PHP script every 10/5 minutes with cron, and then use PHP's time and date functions to perform the rest of the logic. If you cannot time it, you could store a position marking variable in a small file.

Categories