Reading and comparing lines in a file using Python - python

I have a file of the following format.
15/07/2010 14:14:13 changed_status_from_Offline_to_Available
15/07/2010 15:01:09 changed_status_from_Available_to_Offline
15/07/2010 15:15:35 changed_status_from_Offline_to_Away became_idle
15/07/2010 15:16:29 changed_status_from_Away_to_Available became_unidle
15/07/2010 15:45:40 changed_status_from_Available_to_Away became_idle
15/07/2010 16:05:40 changed_status_from_Away_to_Available became_unidle
15/07/2010 16:51:39 changed_status_from_Available_to_Offline
20/07/2010 13:07:26 changed_status_from_Offline_to_Available
I need to create a function in python that has to arguments: date and time. It should read the file and return the second status if the date matches and time is less than the time in the function call. That is
Lets say i call the function returnstatus(15/07/2010, 15:10:01).
The function should go to the file and return the status of the user on that day at that time, which in this case is "Offline".
I am a Python newbie and any help would be really appreciated.

import datetime
import time
def lines( path_to_file ):
'''Open path_to_file and read the lines one at a time, yielding tuples
( date of line, time of line, status before line )'''
with open( path_to_file ) as theFile:
for line in theFile:
line = line.rsplit( " ", 1 )
yield (
datetime.datetime.strptime( line[ 0 ], "%d/%m/%Y %H:%M:%S" ),
line[ 1 ].split( "_" )[ 3 ]
)
def return_status( statDate ):
for lineDate, lineStatus in lines( path_to_file ):
if statDate > lineDate:
continue
return lineStatus
Does that make sense, or would you like me to explain any of it?
Edit
Did you mean what you said above?
date matches and time is less than the time in the function call
In other words, what should happen if you call return_status( 16/07/2010, <some.time> )? Should you get "Offline"?
Another Edit
I have edited it to do sensible datetime comparisons. I think you have read the inequality the wrong way around: we loop through lines in the file until the first line after the date we wish to fetch (keep reading while statDate > lineDate). Once this test fails, line is the first line after the desired date, so its from value is the status at the time we requested. You should call the function with a datetime.datetime.

I suggest you have a read in the python docs, specifically the time module and the function strptime which can parse textual representation of times into a programmatic representation.
Calling returnstatus the way you wrote in the question will surely fail, you might want to call it with a string representation of the time (i.e. "15/07/2010 15:10:01") or by passing one of the datatypes defined in the time module.
EDIT: obviously if you pass in a string time then finding it in the file is much easier:
if substring in line:
# do stuff

As Yoni said, you're probably better served by passing a string argument (if you have one). You may also find the types in datetime useful. You'll also want to look into the split function.

Basically, what you need to do is pull out the dates and times from your log into a easy-to-compare format. Enter datetime.
import datetime
def getStatus(log_list, dt, tm):
#filter the list
log_list = [a_log_entry for a_log_entry in log_list if a_log_entry[0] == dt and a_log_entry[1] <= tm]
#sort it
log_list.sort(cmp=lambda x,y: cmp(x[1], y[1]))
if log_list is []:
return 'No status available for this day and time.'
#pull out the status
status_to_return = log_list[-1][2].split('_')[-1].strip()
return status_to_return
if __name__ == '__main__':
in_file = open('a.log', 'rU')
a_list = []
for line in in_file:
if line.strip() is not '': #handle whitespace
a_list.append(line.split(' '))
#convert string dates and times to datetime objects
a_list = [ [datetime.datetime.strptime(el[0], '%d/%m/%Y'),
datetime.datetime.strptime(el[1], '%H:%M:%S'),
el[2]] for el in a_list]
a_date = datetime.datetime(2010, 7, 15)
a_time = datetime.datetime(1900, 1, 1, 16, 1, 0)
print getStatus(a_list, a_date, a_time)

Try this:
import datetime
filein = open("filein", "r")
class Status:
def __init__(self, date, time, status):
print date.split('/')
day, month, year = map(int, date.split('/'))
hour, minute, second = map(int, time.split(':'))
self.date_and_time = datetime.datetime(year=year, month=month, day=day, hour=hour, minute=minute, second=second)
self.status = status
list = []
line = filein.readline().rstrip('\n')
while line != "":
print line
date, time, status = line.split(' ')[:3]
status = status.split('_')
status.reverse()
status = status[0]
status_it = Status(date=date, time=time, status=status)
line = filein.readline().rstrip('\n')
list.append(status_it)
def query (date, time):
day, month, year = map(int, date.split('/'))
hour, minute, second = map(int, time.split(':'))
date_and_time = datetime.datetime(year=year, month=month, day=day, hour=hour, minute=minute, second=second)
for counter, it in enumerate(list):
if date_and_time >= it.date_and_time and (date_and_time < list[counter + 1].date_and_time or counter == len(list) - 1):
print it.status
return
print "I don't know the status"
query("15/07/2010", "15:10:01")

From the question user392409 most probably wants to pass the parameters as string and wants a single function.
Lets say i call the function returnstatus(15/07/2010, 15:10:01). The function should go to the file and return the status of the user on that day at that time, which in this case is "Offline".
import datetime
import time
def returnstatus(d, t):
d = datetime.datetime.strptime(d, "%d/%m/%Y")
t = time.strptime(t, "%H:%M:%S")
f = open("log.txt")
for line in f:
line = line.split(" ")
line_date = datetime.datetime.strptime(line[0], "%d/%m/%Y")
line_time = time.strptime(line[1], "%H:%M:%S")
if d != line_date and t >= line_time:
continue
# Returns the first occurrence. To get all store in a list or print.
f.close()
return line[2].split("_")[3]

Related

Data Validation inside a function - Python

I am running into an issue with validating an input is a valid date, and if not coming back to the question for a retry.
I want to loop through this header and ask for input for each item.
header = [employee_id,name,address,ssn,date_of_birth,job_title,start_date,end_date]
The CSV is empty aside from the header, as I am appending these rows into via this program. I want the date_validator() to work for DOB, start_date and end_date, but so far i can get it to validate that the input is wrong, it just doesnt go back and ask for the input again.
Any help would be appreciated! thanks!
import csv
import datetime
def add_employee():
global date_answer
list = []
for i in range(len(header)):
var = header[i]
answer1 = input('Input Employees {}:'.format(var))
if "date" in header[i]:
date_answer = answer1
date_validater()
list.append(answer1)
with open('employees.csv','a',newline="") as f_object:
writer = csv.writer(f_object)
writer.writerow(list)
f_object.close()
print()
def date_validater():
# input date
date_string = date_answer
date_format = '%m/%d/%Y'
try:
dateObject = datetime.datetime.strptime(date_string, date_format)
print(dateObject)
except ValueError:
print("Incorrect data format, should be MM/DD/YYYY")
A couple hints:
Functions take parameters...
Globals are a terrible ideas most of the time. Avoid like the plague.
Pass the variables to the function to use it inside the function.
import csv
import datetime
def add_employee(header):
list = []
for i in range(len(header)):
var = header[i]
answer1 = input('Input Employees {}:'.format(var))
if "date" in header[i]:
date_validater(answer1)
list.append(answer1)
with open('employees.csv','a',newline="") as f_object:
writer = csv.writer(f_object)
writer.writerow(list)
f_object.close()
print()
def date_validater(date_string):
# input date
date_format = '%m/%d/%Y'
try:
dateObject = datetime.datetime.strptime(date_string, date_format)
print(dateObject)
except ValueError:
print("Incorrect data format, should be MM/DD/YYYY")
or something like that...

Filter icalendar events based on a time frame breaks because of datetime comparison

I am trying to pull events from an ics file within the next month but making my own datetime and comparing to the datetime in the ics file doesn't seem to be working and is giving the error TypeError: can't compare offset-naive and offset-aware datetimes
I tried the answers found here but still get the same error. Below is the code I am using.
def read_from_cal():
g = open('calendar.ics', 'rb')
gcal = Calendar.from_ical(g.read())
year = datetime.now().year
month = datetime.now().month
day = datetime.now().day
hour = datetime.now().strftime("%H")
minute = datetime.now().strftime("%M")
next_month = datetime(int(year), int(month)+1, int(day), int(hour), int(minute), 0, tzinfo=pytz.UTC)
#next_month = next_month.replace(tzinfo=pytz.UTC)
for component in gcal.walk():
if component.name == "VEVENT":
# time printed out in format:
# year-month-day hour:min:sec
summ = component.get('summary')
start = component.get('dtstart').dt
end = component.get('dtend').dt
if now <= start <= next_month:
print("Worked")
print(summ, start, end)
I've tried both with using replace to change my time to utc and just putting it in the next_month variable itself and they both give the same error above.
I've also tried this and this to no avail.
I have tried with a .ics file generated here, so could not be the same problem, but in some cases start is a datetime.datetime and in other cases is a datetime.date.
This solution worked with my .ics file
from icalendar import Calendar
from datetime import datetime
from dateutil.relativedelta import relativedelta
def read_from_cal():
g = open('example.ics', 'rb')
gcal = Calendar.from_ical(g.read())
today = datetime.now()
next_month = today + relativedelta(months=1)
for component in gcal.walk():
if component.name == "VEVENT":
summ = component.get('summary')
start = component.get('dtstart').dt
end = component.get('dtend').dt
if isinstance(start, datetime):
start = start.replace(tzinfo=None)
if start <= next_month:
print("Worked (datetime)")
print(summ, start, end)
else:
# some events are stored as a date
if start <= next_month.date():
print("Worked (date)")
print(summ, start, end)
read_from_cal()

how to substract two dates in django?

i am trying to subtracts 'end time' of a ride from its 'start time'. starttime is fetched directly from database(models.py) and line 'start = n[0].driverStarttime' indicates that. Now i use current datetime as 'endtime' of a ride. Variable 'diff' is used to subtract end and start time. but it gives
'TypeError at /driver_panel/endtrip
can't subtract offset-naive and offset-aware datetimes' error. here driver_panel is my application in project. Driverbooking table is used to fetch start time. DateTimeField is used for store start and endtime. here is the code...
def endtrip(request):
if request.method == 'GET':
dbid = request.GET.get('driverBookID')
if dbid:
n = Driverbooking.objects.all().filter(driverBookID=dbid)
name = n[0].customerID
start = n[0].driverStartTime
end = datetime.datetime.now()
diff = end - start
total = diff * 10
a = Driverbooking.objects.get(driverBookID=dbid)
a.driverStatus = "end"
a.driverEndTime = end
a.driverAmount = total
a.save()
did = request.session['uid']
x = Driverside.objects.all().filter(driverID=did)
rate = x[0].driverFPH
d = Driverside.objects.get(driverID=did)
d.driverIsAvailable = "yes"
d.save()
context = {"name":name,"start":start,"end":end,"rate":rate,"t":total}
return render(request, "driverbill.html", context)
return redirect('driverhome')
The problem raises because you seem to be subtracting "start time" (which probably has timezone info) and "end time" (which has no timezone).
Simple solution is to use the correct current time with timezone, like the timezone docs suggest.
Instead of this line:
end = datetime.datetime.now()
try this:
from django.utils import timezone as tz
end = tz.now()

Mutiple API with different variable in URL

I am learning Python and had a question regarding for and if loops. This is my scenario:
I have an endpoint that i make API-call with request.get
I need to retrieve all the historic data
I have a start_date (2017-06-17)
So i need to make multiple API-call because they have a limit of 60-days period. So i made my code like this:
date = datetime.strptime("2017-06-17", "%Y-%m-%d") # Start Date
current_date = date.date() # timedelta need date object so i make it a date object
days_after = (current_date+timedelta(days=60)).isoformat() # days_after is set to 60-days because limit in API
date_string = current_date.strftime('%Y-%m-%d') # made to string again since API need string not date object
So this is how i make the dates for 60 days period. Starting from 2017-06-17 and 60-days ahead.
This is how i make the API-request:
response = requests.get("https://reporting-api/campaign?token=xxxxxxxxxx&format=json&fromDate="+date_string+"&toDate="+days_after)
response_data = response.json() # Added this because i am writing temprorary to a JSON file
This is how i write to JSON file:
if response_data:
print("WE GOT DATA") # Debugging
data = response.json() # This is duplicate?
with open('data.json', 'w') as f: # Open my data.json file as write
json.dump(data, f) # dumps my json-data from API to the file
else:
print("NO DATA") # Debugging if no data / response. Should make a skip statement here
So my question is how can i proceed with my code so that every time i make a API-call starting from 2017-06-17 the date date_string and days_after should go 60 days forward for each API-call and append those data to data.json. I would maybe need some for loops or something?
Please note i have been using Python for 3 days now, be gentle.
Thanks!
You could use a while loop that changes the start and end date until a specified condition is met. Also, you can append the response to a file for every run. the example below I used the date of "today":
import os
from datetime import datetime, timedelta
x = 0
y = 60
date = datetime.strptime("2017-06-17", "%Y-%m-%d")
current_date = date.date()
date_start = current_date+timedelta(days=x)
while date_start < datetime.now().date():
date_start = current_date+timedelta(days=x)
days_after = current_date+timedelta(days=y)
x = x + 60
y = y + 60
response = requests.get("https://reporting-api/campaign?token=xxxxxxxxxx&format=json&fromDate="+date_start.isoformat() +"&toDate="+days_after.isoformat())
response_data = response.json()
if response_data:
print("WE GOT DATA")
data = response.json()
#create a file if not exists or append new data to it.
if os.path.exists('data.json'):
append_write = 'a' # append if already exists
else:
append_write = 'w' # make a new file if not
with open('data.json', append_write) as f:
json.dump(data, f)
else:
print("NO DATA")
Basically, on every run the time of start and end is increased by 60 days and appended to the data.json file.

parse dates with icalendar and compare to python datetime

I have an .ics file from which I would like to extract all of the events that occur on today's day. I think I'm having trouble converting the icalendar DTSTART and DTEND to python datetimes. I've tried to follow the documentation at icalendar.readthedocs.org. The list I'm getting is empty, which should not be the case.
This is my code:
import urllib2
import json
from datetime import datetime
from icalendar import Calendar, Event, vDatetime
def getTodayEvents(icsFile):
cal = Calendar.from_ical(icsFile)
today = datetime.now().date()
entries = []
for event in cal.walk('VEVENT'):
dtstart = event['DTSTART']
dtend = event['DTEND']
start = vDatetime.from_ical(dtstart) //Trouble here?
end = vDatetime.from_ical(dtend)
if start <= today <= end:
entry = {'summary' : event['SUMMARY'] }
entries.append(entry)
output = json.dumps(entries)
return output //This list is empty
And this is what the and ics entry looks like:
BEGIN:VEVENT
SUMMARY:Jonny Smith
DTSTART;VALUE=DATE:20140731
DTEND;VALUE=DATE:20150802
UID: 12345
CLASS:PUBLIC
PRIORITY:5
DTSTAMP:20141006T160145Z
TRANSP:OPAQUE
STATUS:CONFIRMED
SEQUENCE:0
LOCATION:Mansfield\, GA
X-MICROSOFT-CDO-APPT-SEQUENCE:0
X-MICROSOFT-CDO-BUSYSTATUS:FREE
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
X-MICROSOFT-CDO-ALLDAYEVENT:TRUE
X-MICROSOFT-CDO-IMPORTANCE:1
X-MICROSOFT-CDO-INSTTYPE:0
X-MICROSOFT-DISALLOW-COUNTER:FALSE
END:VEVENT
DTSTART, DTEND properties have .dt attribute:
#!/usr/bin/env python
import json
from datetime import date
import icalendar # $ pip install icalendar
today = date.today()
calendar = icalendar.Calendar.from_ical(ics_file)
entries = [dict(summary=event['SUMMARY'])
for event in calendar.walk('VEVENT')
if event['DTSTART'].dt <= today <= event['DTEND'].dt]
print(json.dumps(entries, indent=2, sort_keys=True))
Output
[
{
"summary": "Jonny Smith"
}
]
The event object has a method .decoded(), which gives you either a datetime.date object (as in your case, the .ics only has a date) or a datetime.datetime object. For the datetime.datetime object, you additionally need to convert the correct timezone.
In order to make a unified comparison, I convert everything to a string and then compare the string. This ended up, that I wrote an isEventToday method:
from datetime import datetime, timezone, timedelta
def isEventToday(event):
if event.get('dtstart') == None:
dtstart = ""
else:
temp = event.decoded('dtstart')
if isinstance(temp, datetime):
dtstart = temp.astimezone().strftime("%Y-%m-%d")
else:
dtstart = temp.strftime("%Y-%m-%d")
if event.get('dtend') == None:
dtend = ""
else:
temp = event.decoded('dtend')
if isinstance(temp, datetime):
dtend = temp.astimezone().strftime("%Y-%m-%d")
else:
# dtend for day events is the day AFTER the event, so we
# need to substract one!
dtend = (temp - timedelta(days=1)).strftime("%Y-%m-%d")
today = datetime.today().date().strftime("%Y-%m-%d")
if dtstart != "" and dtstart == today:
return True
if dtend != "" and dtend == today:
return True
if dtstart != "" and dtend != "" and dtstart <= today and today <= dtend:
return True
return False
The code does not look nice to me, but it is working.
Check to see if you've got a discrepancy between data types or content in your if start <= today <= end: comparison. Take a look (in debugger or so) at what are the types and content of those three variables. I think you'll find that the comparison is comparing things that are legal to compare, but not compatible enough to give you the answer you expect (i.e., do the start and end times of this event overlap todays date?)
Your today is a datetime structure, which can be compared to other datetimes as you intend. Perhaps your vDatetime.from_ical(dtstart) is returning something other than a datetime. A quick glance at the source looks like it should be returning a datetime though. Maybe you've got a time zone issue? Look at the content of all three and check which is < or == or > others.
If that's the case, add a time zone to your calls to vDatetime.from_ical() calls;
start = vDatetime.from_ical(dtstart,'Europe/Amsterdam') #or wherever you are
Your time in the .ics indicates Z -- i.e., GMT.
If you need to do more with dates, see working with time.

Categories

Resources