Checking array for duplicate of 2 objects - php

I have a small program sunning that reads in a csv file with customers and their bookings and places them into a nested array, I have already sorted some of the data, but now need to make sure that there isn't any customers booking for the same date and time (also is it possible to check that the time is at least 30 min apart, all the times are in 30 min increments) I've tried a simple in_array method and got nothing tried a few other ways and didn't get any where, any ideas how I could do this. $date[5] is the dates and $data[6] is the time, the data is all strings.
if (($h = fopen("{$filename}", "r")) !== FALSE) {
//each line in the file is converted into its own individual array
while (($data = fgetcsv($h, 1000, ",")) !==FALSE) {
//check if anyone has tried to book for unavailable dates, weekends, last day of the month etc
if (!in_array($data[5], $Adates)) {
$contact[] = $data;
}
//this checks if the person is trying to make a booking before 9am or after 6pm
elseif ((($data[6]) < "09:00") || (($data[6]) > "18:00")) {
$contact[] = $data;
} else {
$College1[] = $data;
}
if (in_array($data[5], $College1)
&& in_array($data[6],$College1)) {
$College2[] = $College1;
}
Heres a sample of some of the test data i used (there is over 30 customers) :
T1,Aleshia,Tomkiewicz,atomkiewicz#hotmail.com,01835-703597,03/11/2020,11:30,P256
T2,Evan,Zigomalas,evan.zigomalas#gmail.com,01937-864715,12/11/2020,15:00,P146
T3,France,Andrade,france.andrade#hotmail.com,01347-368222,30/11/2020,08:30,P68

In regards to your code, I guess you could improve your elseif manipulating the time string, getting just the hour and comparing that number as an int.
elseif ((($data[6]) < "09:00") || (($data[6]) > "18:00"))
$hrBooked = (int) explode(':', $data[6])[0]; // This should be placed before the main if
elseif (( $hrBooked < 9) || $hrBooked > 18))
Looking at your code, I think you'll benefit from using the DateTime PHP class for all your checks and comparisons.

Related

Iterating through a column in a CSV file (PHP)

I need to write a function that takes a year and a temperature as an input and returns how many days in a given year the temperature was equal to or below the given temperature. Since the data is about hours, not days, need to find the number of hours and divide it by 24.
Example: getDaysUnderTemp(2019, -10) returns 13.92.
CSV file looks like this (year, month, day, hour, temperature):
2019,1,1,0,0.1
2019,1,1,1,0.4
2019,1,1,2,0.8
2019,1,1,3,1.3
2019,1,1,4,1.8
...
2020,1,1,0,-3.9
So far my code looks like this (I'm new to php):
function getDaysUnderTemp(int $targetYear, float $targetTemp): float {
$inputFile = fopen("data/temperatures-debug.csv", "r");
while (!feof($inputFile)) {
$file = fgetcsv($inputFile);
$hours = intval($file[3]);
if (intval($file[0]) === $targetYear and floatval($file[4]) <= $targetTemp) {
???
return ??? / 24;
}
fclose($inputFile);
}
return "error";
}
The problem is I don't know how to iterate through a column which represents the hours of a specific year, and then add them all together.
you are not far from the solution.
function getDaysUnderTemp($targetYear, $targetTemp): float
{
$hours = 0;
if ((($handle = fopen("data.csv", "r")) !== FALSE)) {
while (($data = fgetcsv($handle)) !== FALSE) {
// [0] -> year [1] -> month [2] -> day [3] -> hours [4] -> temp
if ($data[0] == $targetYear && $data[4] <= $targetTemp) {
$hours += $data[3];
}
}
fclose($handle);
}
return $hours / 24;
}
The logic you had was correct, I just refactored the code and fixed some small issues:
Your function is bound to return a float value but since in your code you are returning the string "error" when a match is not found It would have thrown an exception at some point. I am now returning $hours / 24 so now the problem is solved;
After each iteration, you close the pointer to file by calling fclose($inputFile), this can cause some problems since the feof function needs a valid stream not closed to work properly. Now the fclose function is called only once when the work is done;
For printing the results I suggest you to use the printf function as follows:
printf("%.2f", getDaysUnderTemp(2019, -10);

How to run php file for only 10 times a day without cron?

I am new to php so please mind if it easy question. I have a php script, I want it to be executed only 10 times a day and not more than that. I don't want to use cron for this. Is there any way to do this in php only?
Right now I have set a counter which increases by one every time any one runs the script and loop it to 10 times only. if it exceeds it it shows an error message.
function limit_run_times(){
$counter = 1;
$file = 'counter.txt';
if(file_exists($file)){
$counter += file_get_contents($file);
}
file_put_contents($file,$counter);
if($counter > 11 ){
die("limit is exceeded!");
}
}
I want some efficient way to do this so everyday the script is only executed for 10 times and this is applicable for everyday i.e this counter gets refreshed to 0 everyday or is there any other efficient method.
I would rather recommend that you use a database instead - its cleaner and more simple to maintain.
However, it is achievable with file-handling as well. The file will be of format 2019-05-15 1 (separated by tab, \t). Fetch the contents of the file and split the values by explode(). Then do your comparisons and checks, and return values accordingly.
function limit_run_times() {
// Variable declarations
$fileName = 'my_log.txt';
$dailyLimit = 10;
$content = file_get_contents($fileName);
$parts = explode("\t", $content);
$date = $parts[0];
$counter = $parts[1] + 1;
// Check the counter - if its higher than 10 on this date, return false
if ($counter > $dailyLimit && date("Y-m-d") === $date) {
die("Daily executing limit ($dailyLimit) exceeded! Please try again tomorrow.");
}
// We only get here if the count is $dailyLimit or less
// Check if the date is today, if so increment the counter by 1
// Else set the new date and reset the counter to 1 (as it is executed now)
if (date("Y-m-d") !== $date) {
$counter = 1;
$date = date("Y-m-d");
}
file_put_contents($fileName, $date."\t".$counter);
return true;
}

Calculate g-force from acceleration for 1 second interval

I have extracted a CSV file with accelerometer data (in m/s2) from GoPro metadata file (github library).
One second of accelerometer contains ~200 samples of data on 3 axis. A sample of this file looks like this:
In PHP, for each instantaneous value on X axis, I convert m/s2 like this:
function convert_meters_per_second_squared_to_g($ms2) {
// 1g = 9.80665 m/s2
return $ms2 * 0.101971621297793; // 1 / 9.80665 == 0.101971621297793
}
Sample code for 200 rows (1 second) of CSV file:
$acc_x_summed_up = 0;
if (($handle = fopen($filepath, "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
list ($millis, $acc_x, $acc_y, $acc_z) = $data;
$acc_x_summed_up += $acc_x;
}
}
$g_force = convert_meters_per_second_squared_to_g($acc_x_summed_up);
But how do I show the g-force value for each second on X axis? I tried to sum up the values and convert them, but the result is clearly wrong, as I get values up to 63 G.
[ UPDATE: ]
The instant g-force values (all 3 axis, separated) are displayed on a graph (using highcharts). The gopro video file is displayed (using YouTube javascript API) side-by-side with the graph and played real time.
The graph and video are already working fine side by side. Only the g-force values are wrong.
Note: The video file has a g-force overlay (embeded in it) showing 2 axis (x,y).
I have rewarded #Joseph_J just because it seemed a good solution and because I'm forced to give the reward (over the weekend) by SO system. Thanks everyone for your answers!
I believe you are treating each instantaneous value as if it has occurred over 1 second, rather than instantaneously.
I'd say your best bet is to do each calculation by multiplying $acc_x by the resolution of your data divided by gravity's acceleration. So in your case, the resolution of your data is 5ms or one two-hundredth of a second, meaning your calculation should be $acc_x * 0.005/9.80665.
Using the information you provided, the 63G result that you got should be more like 0.315G. This seems more appropriate, though I'm not sure the context of the data.
EDIT: I forgot to mention that you should still sum all values that you receive from $acc_x * 0.005/9.80665 over 200 values, (you can choose to do this in blocks, or do it in running, doing in blocks will be less taxing on the system, but running will be more accurate). Pointed out by #Joseph_J
EDIT 2: As per your request of a source, I could not find much from calculating the average acceleration (and therefore g-force), but you can use the same principal behind average velocity from velocity over time graphs, however, I did find a scenario similar to yours here: Source and Source 2
Hope this helps!
As per my comment, summing it up doesn't work because force is not additive over time. What you want is to calculate the average acceleration:
function convert_meters_per_second_squared_to_g($acc_array) {
$acc_average = array_sum($acc_array)/count($acc_array);
return $acc_average * 0.101971621297793;
}
$acc_x_array = [];
if (($handle = fopen($filepath, "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
list ($millis, $acc_x, $acc_y, $acc_z) = $data;
$acc_x_array[] = $acc_x;
}
}
$g_force = convert_meters_per_second_squared_to_g($acc_x_array);
Maybe your question can be seen as equivalent to asking for the net change in velocity between samples at one-second intervals?
In that sense, what you need to do is to integrate-up all the small accelerations in your 5ms intervals, so as to compute the net change in velocity over a period of one second (i.e. 200 samples). That change in velocity, divided by the 1-second interval, represents an average acceleration during that 1-second period.
So, in your case, what you'd need to do is to add up all the AcclX, AcclY & AcclZ values over a one-second period, and multiply by 0.005 to get the vector representing the change in velocity (in units of metres per second). If you then divide that by the one-second total extent of the time window, and by 9.80665m/s^2, you'll end up with the (vector) acceleration in units of G. If you want the (scalar) acceleration you can then just compute the magnitude of that vector, as sqrt(ax^2+ay^2+az^2).
You could apply the same principle to get an average acceleration over a different time-window, so long as you divide the sum of AcclX,AcclY,AcclY (after multiplying by the 0.005s inter-sample time) by the duration of the time window over which you've integrated. This is just like approximating the time-derivative of a function f(t) by (f(t+d) - f(t))/d. In fact, this is a better approximation to the derivative at the midpoint of the time-interval, namely t+d/2. For example, you could sum up the values over a 2s window, to get an average value at the centre of that 2s timespan. There's no need to just report these average accelerations every two seconds; instead you could simply move the window along 0.5s to get the next reported average acceleration 0.5s later.
THE UPDATED UPDATED SOLUTION
This solution will take your CSV and create an array containing your time, Ax, Ay, & Az values after they have been converted to G's. You should be able to take this array and feed it right into your graph.
The value displayed at each interval will be the average acceleration "at" the interval no before or after it.
I added a parameter to the function to allow for you to define how many intervals per second that you want to display on your graph. This will help smooth out your graph a bit.
I also set the initial and final values. Since this finds the average acceleration at the interval it needs data on both sides of the interval. Obviously at 0 we are missing the left hand side and on the last interval we are missing the right hand side.
I chose to use all the data from one interval to the next, this overlaps half the values from one interval to the next. This will smooth out(reduce the noise) of the averages instead of pickup up from one interval where the other left off. I added a parameter where you can toggle the overlap on and off.
Hope it works for you!
function formatAccelData($data, $split, $scale, $overlap = TRUE){
if(!$data || !$split || !$scale || !is_int($split) || !is_int($scale)){
return FALSE;
}
$g = 9.80665;
$round = 3;
$value1 = 1;
$value2 = 2;
if(!$overlap){ //Toggle overlapping data.
$value1 = 2;
$value2 = 1;
}
//Set the initial condition at t=0;
$results = array();
$results[0]['seconds'] = 0;
$results[0]['Ax'] = round(($data[0][1])/$g, $round);
$results[0]['Ay'] = round(($data[0][2])/$g, $round);
$results[0]['Az'] = round(($data[0][3])/$g, $round);
$count = 1;
$interval = (int)(1000/$split)/$scale;
for($i = $interval; $i < count($data); $i += $interval){
$Ax = $Ay = $Az = 0;
for($j = $i - ($interval/$value1); $j < $i + ($interval/$value1); $j++){
$Ax += $data[$j][1];
$Ay += $data[$j][2];
$Az += $data[$j][3];
}
$results[$count]['seconds'] = round($count/$scale, $round);
$results[$count]['Ax'] = round(($Ax/($interval * $value2))/$g, $round);
$results[$count]['Ay'] = round(($Ay/($interval * $value2))/$g, $round);
$results[$count]['Az'] = round(($Az/($interval * $value2))/$g, $round);
$count++;
}
array_pop($results); //We do this because the last interval
//will not have enought data to be calculated.
//Set the final condition with the data from the end of the last complete interval.
$results[$count - 1]['seconds'] = round(($count - 1)/$scale, $round);
$results[$count - 1]['Ax'] = round(($data[$i - $interval][1])/$g, $round);
$results[$count - 1]['Ay'] = round(($data[$i - $interval][2])/$g, $round);
$results[$count - 1]['Az'] = round(($data[$i - $interval][3])/$g, $round);
return $results;
}
To use:
$data = array_map('str_getcsv', file($path));
$split = 5; //(int) - # of milliseconds inbetween datapoints.
$scale = 4; // (int) # of data points per second you want to display.
$overlap = TRUE; //(Bool) - Overlap data from one interval to the next.
$results = formatAccelData($data, $split, $scale, $overlap);
print_r($results);
THE OLD UPDATED SOLUTION
Remember, this function takes the average leading up to the interval. So it's really a half an interval behind.
function formatAccelData($data, $step){
$fps = 1000/$step;
$second = 1;
$frame = 0;
$count = 0;
for($i = 0; $i < count($data); $i += $fps){
$Ax = $Ay = $Az = 0;
for($j = 0; $j < $fps; $j++){
$Ax += $data[$frame][1];
$Ay += $data[$frame][2];
$Az += $data[$frame][3];
$frame++;
}
$results[$count]['seconds'] = $second;
$results[$count]['Ax'] = ($Ax/$fps) * 0.101971621297793;
$results[$count]['Ay'] = ($Ay/$fps) * 0.101971621297793;
$results[$count]['Az'] = ($Az/$fps) * 0.101971621297793;
$second++;
$count++;
}
return $results;
}
How to use:
$data = array_map('str_getcsv', file($path));
$step = 5; //milliseconds
$results = formatAccelData($data, $step);
print_r($results);

PHP Auto Aging Task for MyBB

So, I was wondering if anybody would mind checking over this task and correcting it? I'm very sure I've muddled Python in with what little PHP I know, and that there are open tags.
Basically, there'll be a field where the nasty decimal age goes ($age, which will later be replaced by the appropriate field id). Our site works in months for juveniles and then years and seasons for adults. Using the nasty age, I'm trying to calculate the rounded age values and then store them as a string value which will then be set as the value of the field that will display the age ($displayagefid, will be replaced later with the appropriate field id). Only certain usergroups will be updated (the list is huge, so I left it out).
I also have no idea how to set a variable as a string using both string and the value of another variable.
Please know that I'm a complete newbie to PHP.
This is intended to run as a task on a self-hosted MyBB forum.
Thank you in advance!
<?php
function task_age($task)
{
global $mybb, $db;
$increment = 0.04167
$age = $age + $increment
floor($age*4) = $seasons
floor($age) = $years
floor($age*12) = $months
if ($year < 1) {
$display_age = $months, "mnths"
}
elseif ( ! filter_var($year, FILTER_VALIDATE_INT) ){
$display_age = $year, "yrs"
}
else {
$display age = $display_age = $years, "yrs", $seasons, "s"
};
$query = $db->query("
UPDATE mybb_userfields userfields
for ($usergroup = a || b || d || all that other crap) {
SET $dispalyagefid = $display_age;
};
");
add_task_log($task, "The age characters task successfully ran.");
I had a cursory look over your code and the first thing which sticks out is you have some of your variable assignments back to front:
$increment = 0.04167
$age = $age + $increment
floor($age*4) = $seasons
floor($age) = $years
floor($age*12) = $months
Whatever is on the left gets set to whatever is on the right, so your first two are OK but the last three need switching around.
Having said that it seems to me you are not approaching this correctly. I enter my decimal age into your site but how are you going to work out seasons? It might be my birthday tomorrow, it might have been my birthday yesterday.
You would be better off having the user enter a date of birth, from that calculate their age.
$birthday=date_create("2013-03-15");
$today=date_create('now');
$diff=date_diff($birthday,$today);
Now in the $diff variable you can check all the elements of a PHP date. So first check if they are under 18:
if ($diff->format("%y") < 18) {
$ageInMonths = ($diff->format("%y") * 12) + $diff->format("%m");
$age = "$ageInMonths months";
}
If they are over 18 you want age in years, then calculate seasons from the remaining months.
else {
$ageInYears = $diff->format("%y");
$ageInSeasons = floor($diff->format("%m") / 4);
if ($ageInSeasons > 0) {
$age = "$ageInYears years and $ageInSeasons seasons";
} else {
$age = "$ageInYears years";
}
}

Finding the nearest data point and sunshine data in php

I am in a big problem.The problem is following
1)I have a .dat file(dat file used to store the data ).It is a 25 MB file which contains
over 200K rows of latitude and longitudes.Example of one of such row is
-59.083 -26.583 9.4 5.2 3.3 4.3 8.1 6.6 5.3 8.4 8.3 10.0 9.1 5.1
It statrs form the latitude ,longitude,sunshine data (hours) in january,fabruary..and so on to decmber.
I have more than 200K rows like above.
My task is to calculate the sunshine hours in a particulr latitude and longitude.Suppose i have a latitude =3.86082 and longitude=100.50509 ;.So my task would be to find average sunshine hours per month on this latitude and longitude.but the second problem is that i am not going to have exact match of given latitude and longitude with the ones i have in the file.So first of all i have to find the nearest point and then i have to calculate the sunshine hours.
I am using the following code to calculate the nearest point.But it is taking a huge time beacuse of the bulk of data in the file
$file_name='grid_10min_sunp.dat';
$handle = fopen($file_name, "r");
$lat1=13.86082;
$lan1=100.50509;
$lat_lon_sunshines = make_sunshine_dict($file_name);
$closest = 500;
for($c=0;$c<count($lat_lon_sunshines);$c++)
{
$lat2=$lat_lon_sunshines[$c]['lat'];
$lan2=$lat_lon_sunshines[$c]['lan'];
$sunshines=$lat_lon_sunshines[$c]['sunshine'];
$lat_diff = abs(round((float)($lat1), 4)-$lat2);
if ($lat_diff < $closest)
{
$diff = $lat_diff + abs(round((float)($lan1), 4)-$lan2);
if($diff < $closest)
{
$closest = $diff;
$sunshinesfinal=$sunshines;
}
}
$sunshines='';
}
print_r($sunshinesfinal);die;
the function make_sunshine_dict($file_name ) also goes throgh each line of the file and prepares an array as following
$sunshines_dict = array();
$f = file_get_contents($file_name);
$handle = fopen($file_name, "r");
while($buffer = fgets($handle))
{
$tok = strtok($buffer, " \n\t");
$lat=$tok;
$latArray[]=$tok;
$tok = strtok(" \n\t");
$months = '';
$months = array();
for ($k = 0; $tok !== false; $k+=1)
{
if($k==0)
{
$lan=$tok;
$lanArray[]=$tok;
}
if($k!=0)
{
$months[] = $tok ;
"month $k : ".$months[$k]."<br>";
}
$tok = strtok(" \n\t");
}
$data[$kkk]['lat']=$lat;
$data[$kkk]['lan']=$lan;
foreach($months as $m=>$sunshine)
{
$sunshines=array();
$sumD = 0;
$iteration= 31;
for($n=1;$n<=$iteration;$n++)
{
$J = ($m+1)*$n;
$P = asin(.39795*cos(.2163108 + 2*atan(.9671396*tan(.00860*($J-186)))));
$value=(sin(0.8333*pi/180) + sin($lat*pi/180)*sin($P))/(cos($lat*pi/180)*cos($P));
/* $value ? ($value > 1 and 1) : $value;
$value ? ($value < -1 and -1): $value;*/
$D = 24 - ((24/pi) * acos($value));
$sumD = $sumD + $D;
}
$sunshinesdata=(($sumD/30)*(float)($sunshine)*.01);
$data[$kkk]['sunshine'][$m]=$sunshinesdata;
$sunshines='';
}
}
return $data;
Please help and please let me know if you require more information
And please remenber i can not use default php function for sunshine information here beacsue i am also taking cloud cover and other factors into consideration
A lot of the code looks wrong (in terms of just figuring out the sunshine hours). Although, I'll admit, I'm lost as to what you are doing in the make_sunshine_dict function.
In terms of helping you speed things up, you are reading your file twice:
$f = file_get_contents($file_name);
$handle = fopen($file_name, "r");
Also, you can map Lat/Long coordinates into a grid. In other words, make a 2 dimensional array where each row and each column represents 1 degree of latitude and 1 degree of longitude, respectively. As you read in your file, dump entries into the correct lat/long bucket in the grid. When you need to find the location closest to a given lat/lon then you only need to compare that location against the points in its bucket.
Load the .dat into a SQL database every x interval in a Cron job and then just query the database like you always would.

Categories