canvasapi icon indicating copy to clipboard operation
canvasapi copied to clipboard

AttributeError: 'str' object has no attribute 'update' when running get_authentication_events

Open technomanxer opened this issue 2 years ago • 6 comments

Describe the bug

I'm getting AttributeError: 'str' object has no attribute 'update' when I try to retrieve & process user.get_authentication_events()

To Reproduce

Steps to reproduce the behavior: Run the following code after setting up Canvas object & retrieving single user

user_logins = user.get_authentication_events(start_time="2022-04-03", end_date="2022-04-04")
for u in user_logins:
    print(u)

The code stops right before the print statement on the "for" line. get_authentication_events() returns a PaginatedList but it's not iterable for some reason. Unless I'm missing something. Debug logs are showing that it retrieves the data from the REST API once I start the for loop (I can supply that log upon request)

Expected behavior

I should be able to loop through this PaginatedList object like every other PaginatedList object in Canvas API.

Environment information

  • Python version (python --version): Python 3.10.2
  • CanvasAPI version (pip show canvasapi): Name: canvasapi Version: 2.2.0 Summary: API wrapper for the Canvas LMS Home-page: https://github.com/ucfopen/canvasapi Author: University of Central Florida - Center for Distributed Learning Author-email: [email protected]

Additional context

Here is the stack trace (sanitized personal info/paths as much as possible)

technomanxer avatar Apr 04 '22 20:04 technomanxer

I just tried and got the same result, testing against myself (so I know I had a login). Here's the method in the User object.

Canvas is returning data from https://{{domain}}/api/v1/audit/authentication/users/:user_id to Postman. I don't have time to look deeper, but you could try setting up a Requester instance and testing it directly. I can try some other testing later this week.

bennettscience avatar Apr 04 '22 21:04 bennettscience

Nevermind, figured it out.

Canvas returns an object with nested links and events objects. The library expects events, but when you iterate the PaginatedList, links doesn't have the correct structure and it can't build the AuthenticationEvent instance.

Because it's returned as a PaginatedList, I can't think of a way to extract the data using this version without iterating directly. You can patch your local version of canvasapi to return JSON (see here for an example of how) until I can get a PR together. Or, write your own function using a Requester instance to handle the API call.

bennettscience avatar Apr 04 '22 21:04 bennettscience

Wow, thank you @bennettscience. Appreciate the insight & prompt response!

I'll probably write my own function to get the API call going for now with Requester.

technomanxer avatar Apr 04 '22 21:04 technomanxer

PaginatedList can be passed a _root keyword argument to tell it what key to build the list from. If you want to keep working with AuthenticationEvent objects instead of parsing JSON, you might be able to get away with something like this:

def get_authentication_events(self, **kwargs):
    """
    List authentication events for a given user.
    :calls: `GET /api/v1/audit/authentication/users/:user_id \
    <https://canvas.instructure.com/doc/api/authentications_log.html#method.authentication_audit_api.for_user>`_
    :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
            :class:`canvasapi.authentication_event.AuthenticationEvent`
    """
    from canvasapi.authentication_event import AuthenticationEvent

    return PaginatedList(
        AuthenticationEvent,
        self._requester,
        "GET",
        "audit/authentication/users/{}".format(self.id),
        _kwargs=combine_kwargs(**kwargs),
        _root="events"
    )
    
canvasapi.user.User.get_authentication_events = get_authentication_events

jessemcbride avatar Apr 04 '22 21:04 jessemcbride

Ah, I forgot about _root! @jessemcbride, I think I've even asked you about that argument in the past.

I'd do that instead ☝️ .

bennettscience avatar Apr 04 '22 22:04 bennettscience

Thanks @jessemcbride. Just FYI your patch now triggers AttributeError: 'AuthenticationEvent' object has no attribute 'pseudonym_id' in canvas_object.py.

I had to go into authentication_event.py https://github.com/ucfopen/canvasapi/blob/9383a97602a9e794eca76397ba4d521f1e983ad9/canvasapi/authentication_event.py#L6 and patch that to the following line: return "{} {} ({})".format(self.created_at, self.event_type, self.id) for the object to be formed.

Otherwise it works great and is returning exactly what I need in the object.

technomanxer avatar Apr 05 '22 11:04 technomanxer