Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTP 400 accessing calendar for room mailbox #364

Open
lia-cha-avalara opened this issue Nov 27, 2019 · 24 comments
Open

HTTP 400 accessing calendar for room mailbox #364

lia-cha-avalara opened this issue Nov 27, 2019 · 24 comments

Comments

@lia-cha-avalara
Copy link

schedule = account.schedule()
calendar = schedule.get_calendar(calendar_name='IT Vacation Calendar')
q = calendar.new_query('start').greater_equal(datetime.datetime(2019, 11, 25))
q.chain('and').on_attribute('end').less_equal(datetime.datetime(2019, 11, 29))

vacations = calendar.get_events(query=q, include_recurring=True)
for event in vacations:
    print(event.subject)

Using the sample in the documentation, I created this test but am getting a "Bad Request" error message saying

Error Message: Resource not found for the segment 'calendars'.

Am I doing something wrong? I've added Calendars.Read application access and Calendar.Read.Shared delegated permissions. Not sure if this is supported but I assume it has something to do with the permissions.

@lia-cha-avalara
Copy link
Author

I actually passed in the accounts email address into the schedule() constructor and it now seems to pass but once it hits the query builder stage I get this:

AttributeError: 'NoneType' object has no attribute 'new_query'

I was expecting a schedule type object to be returned??

@alejcas
Copy link
Member

alejcas commented Nov 27, 2019

Instead of:

q.chain('and').on_attribute('end').less_equal(datetime.datetime(2019, 11, 29))

Do:

q = q.chain('and').on_attribute('end').less_equal(datetime.datetime(2019, 11, 29))

@lia-cha-avalara
Copy link
Author

@janscas - Im not sure that's the problem. It's complaining that the line:
calendar = schedule.get_calendar(calendar_name='IT Vacation Calendar')
is returning an object of NoneType and that object type does not have the new_query() method

@alejcas
Copy link
Member

alejcas commented Nov 28, 2019

the calendar seems to be incorrect then as it's now found..

try:

for calendar in schedule.list_calendars():
    print(calendar.name)
    if calendar.name == 'IT Vacation Calendar':
        print('found!')

You should see 'found!'. If not, check all the printed calendar names to see the correct name.

@lia-cha-avalara
Copy link
Author

so if i pass in a resource into the schedule() constructor I get something like this...
Calendar
And nothing else is printed. If i don't pass in a resource I get the same error as before
raise HTTPError('{} | Error Message: {}'.format(e.args[0], error_message), response=response) from None requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://graph.microsoft.com/beta/calendars | Error Message: Resource not found for the segment 'calendars'.

@alejcas
Copy link
Member

alejcas commented Nov 28, 2019

Can you paste the complete code block of both cases?

@lia-cha-avalara
Copy link
Author

lia-cha-avalara commented Nov 28, 2019

` CLIENT_ID = os.environ['CLIENT_ID_CAL']
CLIENT_SECRET = os.environ['CLIENT_SECRET_CAL']
TENANT_ID = os.environ['TENANT_ID']

def main():
    credentials = (CLIENT_ID, CLIENT_SECRET)
    token_path = "c:\\path\\to\\log\\file.log"
    token_backend = FileSystemTokenBackend(token_path=token_path, 
    token_filename="o365token.txt")
    protocol = MSGraphProtocol(api_version="beta")
    start_time = datetime.now()
    logger.info("Attempting to authenticate with MS Graph API")
    try:
        account = Account(credentials, token_backend=token_backend, protocol=protocol,
                                        auth_flow_type='credentials', tenant_id=TENANT_ID)
        logger.info("Authenticated!")
    except Exception as e:
        logger.error(e)
        sys.exit(1)

schedule = account.schedule()
for calendar in schedule.list_calendars():
    print(calendar.name)
    if calendar.name == 'IT Vacation Calendar':
        print('found!')`

And

` CLIENT_ID = os.environ['CLIENT_ID_CAL']
CLIENT_SECRET = os.environ['CLIENT_SECRET_CAL']
TENANT_ID = os.environ['TENANT_ID']

def main():
    credentials = (CLIENT_ID, CLIENT_SECRET)
    token_path = "c:\\path\\to\\log\\file.log"
    token_backend = FileSystemTokenBackend(token_path=token_path, 
    token_filename="o365token.txt")
    protocol = MSGraphProtocol(api_version="beta")
    start_time = datetime.now()
    logger.info("Attempting to authenticate with MS Graph API")
    try:
        account = Account(credentials, token_backend=token_backend, protocol=protocol,
                      auth_flow_type='credentials', tenant_id=TENANT_ID)
        logger.info("Authenticated!")
    except Exception as e:
        logger.error(e)
        sys.exit(1)

schedule = account.schedule(resource="[email protected]")
for calendar in schedule.list_calendars():
    print(calendar.name)
    if calendar.name == 'IT Vacation Calendar':
        print('found!')`

@lia-cha-avalara
Copy link
Author

weird but if i set main_resource in the Account object to the resource email address and set the get_calendar() method to search for 'Calendar', everything seems to work. I somehow can't leave the main_resource attribute or the resource attribute from the schedule() constructor empty and just run the list_calendars() function to show all calendars. I think specifically hardcoding the resource email address is fine for what I need though. Thanks for all the help

@lia-cha-avalara
Copy link
Author

lia-cha-avalara commented Nov 29, 2019

so i got it working for the most part but I'm noticing some incorrect data being retrieved. My query is set to only retrieve events between 11/25/2019 and 11/29/2019. It does this successfully but it's showing the wrong start and end dates for some events.

Some events are showing up as having a start date of 11/24/2019 4PM to 11/27/2019 4PM but the real event on the calendar is actually from 11/25/2019 12AM to 11/28/2019 12AM.

In addition, some events are showing the following log message:
2019-11-28 20:45:46 - DEBUG - Couldn't parse event response time: 0001-01-01T00:00:00Z

Code block for reference:

`

if account.authenticate():

    logger.info("Authenticated!")
    schedule = account.schedule()
    calendar = schedule.get_calendar(calendar_name='Calendar')

    q = calendar.new_query('start').greater_equal(dt.datetime(2019, 11, 25))
    q.chain().on_attribute('end').less_equal(dt.datetime(2019, 11, 29))

    vacations = calendar.get_events(query=q, include_recurring=True)
    for event in vacations:
        start = event.start.strftime("%m/%d/%Y %H:%M")
        end = event.end.strftime("%m/%d/%Y %H:%M")
        attendees = [attendee.name for attendee in event.attendees]
        print("Subject is: {}, Start Time: {}, End Time: {}, Organizer: {}, Attendees: {}".format(
              event.subject, start, end, event.organizer, attendees))
else:
    logger.info("Not Authenticated")`

@lia-cha-avalara
Copy link
Author

Well I feel really dumb - the main issue above is because it's returning the date values in UTC timezone. I need to convert them to PST. Will look into this on my own since the attempts I've tried have been unsuccessful.

The issue with the "couldn't parse event response" still remains. Any ideas what could be causing that?

@alejcas
Copy link
Member

alejcas commented Nov 29, 2019

The library tries to guess the local timezone and it defaults to UTC if not found.

You can pass a “timezone” to the protocol instantiation to automatically convert all timezones.

For example:

MSGraphProtocol(timezone='Europe/Paris')

@alejcas
Copy link
Member

alejcas commented Nov 29, 2019

Also read this about the include recurring option:

https://github.com/O365/python-o365/blob/master/README.md#notes-regarding-calendars-and-events

@lia-cha-avalara
Copy link
Author

I must be doing something wrong because i changed mine to
protocol = MSGraphProtocol(timezone="America/Los_Angeles")

and then changed the recurring option to False and now I'm getting this:

File "C:\Users\me\Documents\Projects\O365\lib\site-packages\O365\utils\utils.py", line 889, in _parse_filter_word word = self.protocol.timezone.localize( AttributeError: 'str' object has no attribute 'localize'

@alejcas
Copy link
Member

alejcas commented Nov 29, 2019

No, it’s my fault...

Use:

from pytz import timezone

protocol = MSGraphProtocol(timezone=timezone('America/Los_Angeles'))

I’ll commit a change to allow to pass strings to the timezone param.

@lia-cha-avalara
Copy link
Author

something weird must be going on... using the code above i no longer get the AttributeError but it seems like the timezone is only correct for one of the entries. Everything else is still returning the same (ie, start time for 4PM when it should really read 12AM).

Also - not sure if you caught my other thing from an earlier post but I am getting intermittent results that just output the following message:

2019-11-29 11:40:21 - DEBUG - Couldn't parse event response time: 0001-01-01T00:00:00Z

@alejcas
Copy link
Member

alejcas commented Nov 30, 2019

Ok, I’ll investigate that...

About this:

2019-11-29 11:40:21 - DEBUG - Couldn't parse event response time: 0001-01-01T00:00:00Z

It’s just a debug message. When the event has no response (it’s not accepted or whatever) the response time could not be parsed.

@alejcas
Copy link
Member

alejcas commented Dec 5, 2019

something weird must be going on... using the code above i no longer get the AttributeError but it seems like the timezone is only correct for one of the entries. Everything else is still returning the same (ie, start time for 4PM when it should really read 12AM).

I've run some test changing to/from different timezones on a test calendar and It completely works for me:

account = Account(credentials, timezone='Europe/London')
calendar = account.schedule().get_default_calendar()

q = calendar.q('start').equals(dt.datetime(2019, 8, 10)).on_attribute('end').equals(dt.datetime(2019, 8, 30))
for event in calendar.get_events(limit=4, query=q):
    print(event.start, event.end)

This returns (Note the +1 which is the London offset in August as DST apply):

2019-08-13 10:00:00+01:00 2019-08-13 11:00:00+01:00
2019-08-14 10:00:00+01:00 2019-08-14 11:00:00+01:00
2019-08-15 10:00:00+01:00 2019-08-15 11:00:00+01:00
2019-08-16 10:00:00+01:00 2019-08-16 11:00:00+01:00

Now, the same query changing the applied timezone to 'Europe/Paris':

account = Account(credentials, timezone='Europe/Paris')
calendar = account.schedule().get_default_calendar()

q = calendar.q('start').equals(dt.datetime(2019, 8, 10)).on_attribute('end').equals(dt.datetime(2019, 8, 30))
for event in calendar.get_events(limit=4, query=q):
    print(event.start, event.end)

This returns (Note the +2 which is the Paris offset in August as DST apply):

2019-08-13 11:00:00+02:00 2019-08-13 12:00:00+02:00
2019-08-14 11:00:00+02:00 2019-08-14 12:00:00+02:00
2019-08-15 11:00:00+02:00 2019-08-15 12:00:00+02:00
2019-08-16 11:00:00+02:00 2019-08-16 12:00:00+02:00

@lia-cha-avalara
Copy link
Author

lia-cha-avalara commented Dec 8, 2019

I think i was misunderstanding the timezone piece and asking the wrong question. I believe mine is working correctly as well. An example below shows:
Start: 2019-11-26 16:00:00-08:00, End: 2019-11-28 16:00:00-08:00

The actual event in my Outlook is for 11/27 12AM PST to 11/29 12AM PST. The -8:00 offset would mean the 11/26 16:00:00 above would actually translate to 11/27 12AM PST. I guess the "right" question to ask here is how can I convert the output to PST so if i gave this report to someone they could easily identify the start and end time without mentally figuring out 11-26 16:00:00-08:00 is actually 11/27 12AM local time.

I guess i could live with the above situation if there was no way around it but the odd thing is i have 2 other events on the calendar that dont have a start and stop time of 12AM and they are reporting the correct PST time. See correct examples below:

Start: 2019-11-27 15:00:00-08:00, End: 2019-11-27 17:00:00-08:00
Start: 2019-11-28 04:00:00-08:00, End: 2019-11-28 10:00:00-08:00

Both these timestamps match the time for the event on the calendar. Any event that start/end at 12AM is showing the timestamp with the -8hr offset.

@alejcas
Copy link
Member

alejcas commented Dec 8, 2019

event.start and event.end will always be a datetime with a timezone. So you can always get the datetime timezone or convert it to another timezone

@lia-cha-avalara
Copy link
Author

i used something like....
start = event.start.astimezone(timezone("Americas/Los Angeles"))
But I get the same results. I looked through the calendar module source code and the start and end methods seem to indicate that the tzinfo is being pulled from the Protocol class. I already set the timezone there to "Americas/Los Angeles" but I dont see any changes aside from the two entries i noted in my previous comment.

@alejcas
Copy link
Member

alejcas commented Dec 8, 2019

If you define for example your local timezone to be "Europe/London":

account = Account(credentials, timezone="Europe/London")

then all datetimes will be set to this timezone.
You can convert this timezones to other timezones like:

from pytz import timezone

schedule = account.schedule()
message = list(schedule.get_messages())[0]

tz_target = timezone('Europe/Paris')

print(message.start)
print(message.start.astimezone(tz_target))

This will print:

datetime.datetime(2019, 10, 2, 16, 0, tzinfo=<DstTzInfo 'Europe/London' GMT0:00:00 STD>)
datetime.datetime(2019, 10, 2, 15, 0, tzinfo=<DstTzInfo 'Europe/Paris' CET+1:00:00 STD>)

@lia-cha-avalara
Copy link
Author

lol sorry im just dumb....i just realized everything works . The events that were showing up as 16:00 PST is actually the right time in PST time. The event was using Dublin timezone which is 12AM.

@lia-cha-avalara
Copy link
Author

Actually...i did more checks and even some events that are set to PST and have a start time of 12AM are showing up as 16:00-08:00 when I run my script. I'm not sure why only the stuff that's set to start and end at 12AM are affected. All others appear fine.

It made sense for events set with Dublin timezone to show up as 16:00 PST when the event is set to 12AM but if the event timezone is set to Pacific and still shows up as 16:00 PST that to me is clearly wrong?

@alejcas
Copy link
Member

alejcas commented Dec 10, 2019

You may need to provide sample data here.
As I told you I do not observe any estrange behaviour.

Basically the process is always this:

  1. Get data from the api
  2. Get the data timezone (it's always a windows timezone)
  3. Convert from windows timezone to Iana imezone (see timezone conversion)
  4. Convert the data datetime from server timezone to local timezone (or the timezone defined in the account init) using the astimezone method.
  5. Return the datetime that will always be timezone aware.

Maybe the timezone conversion table (point 3) is somehow failing in your case.
To check this you will need to provide the raw data (datetime and timezone) returned by microsoft graph (you can use the graph explorer for this).

Then with this we can compare the correct logical conversion with the library output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants