Git Product home page Git Product logo

canvasapi's People

Contributors

a-goetz avatar allygator avatar altgilbers avatar andrew-gardener avatar anthonyrodriguez726 avatar atarisafari avatar bennettscience avatar danbrink91 avatar deundrewilliams avatar devints47 avatar dmols avatar dsavransky avatar iturgeon avatar jessemcbride avatar jonespm avatar jrsilveti avatar kailukaitisbrendan avatar katherinecrose avatar liblit avatar ljoks avatar matthewf-ucsd avatar mike-nahmias avatar nathaned avatar nottheswimmer avatar qwertynerd97 avatar rowdyrotifer avatar stevenbell avatar thetwam avatar tuanvpham avatar weining-li avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

canvasapi's Issues

canvasapi v0.4.0 Release

Nearing completion on all additions and fixes for v0.4.0.

Merge develop to master and mark the release.

Accept path-like objects for file uploads

Under canvasapi 0.7.0, canvasapi.upload.Uploader.__init__ allows two ways to identify the file to be uploaded: it may be given as a string filename, or as an already-open file object. However, there are other ways to describe paths to files besides just strings: what the Python documentation calls “path-like objects.” For example:

  • From Python 3.4 onward, the pathlib module provides a rich API for manipulating filesystem paths as path-like objects rather than as strings.

  • From Python 3.6 onward, the os.DirEntry objects returned by os.scandir are path-like objects.

As far as I can tell, the Uploader class will not behave correctly if constructed with a path-like object as its file argument. This should be improved so that path-like objects are treated like openable filenames, rather than being assumed to be already-open files.


Python 3.6 added several library features to make it easier to write code that transparently handles both string paths as well as path-like objects:

  • A new os.PathLike abstract base class makes it possible to recognize path-like objects in a generic way, rather than checking for specific concrete classes.

  • A new os.fspath function returns the string representation of a path-like object, or simply returns its argument unchanged if that argument is already a string.

  • Improvements to os.open and other standard library functions allow them to accept either path-like objects or strings as arguments.

The Python 3.6 release notes include more information on how to use these features.

Unfortunately, those Python 3.6 improvements still leave a gap: Python 3.4 and 3.5 have the pathlib module, but do not have os.PathLike, os.fspath, the improvements to os.open, etc. Supporting path-like objects in these earlier Python releases may be inconvenient, or perhaps not worth doing at all.

Parse Nested Objects

Original title was "Canvas.get_assignment_group should turn assignments into Assignment instances"

A call to Course.get_assignment_group can optionally include full details about the assignments in the group. That is a nice way to reduce round-trips if we already know that the details will be needed.

In version 0.6.0 of canvasapi, these assignment details are provided as a list of dict.

group = course.get_assignment_group(group.id, include='assignments')
for assignment in group.assignments:
    print(assignment)  # prints each assignment as a dict

I was expecting them to be a list of Assignment instances. Each dict seemingly has all of the details needed to build an Assignment, and I can do that conversion myself:

group = course.get_assignment_group(group.id, include='assignments')
assignments = [Assignment(group._requester, raw) for raw in group.assignments]
for assignment in assignments:
    print(assignment)  # prints each assignment as an Assignment

However, accessing group._requester directly seems like cheating. And I would have expected canvasapi to take care of such details behind the scenes. Can that be done? Can Course.get_assignment_group convert each dict into a proper Assignment for me? (Obviously it should only do so if assignment details are present; without include='assignments', no details are given, so there is nothing to convert.

Handle other iterable sequences as "param[]=..." parameter values

Issue #55 pointed out that canvasapi 0.6.0 made it awkward to pass a list as a parameter value. Pull request #63 fixed this, with friendly support for both list and tuple from canvasapi 0.7.0 forward. Yay! However, list and tuple are just two of the many Pythonic ways to collect multiple values. It would be nice if canvasapi provided the same parameter-marshaling support for any parameter value that is actually an iterable collection of multiple values: set, generators, iterable objects returned by zip and ichain, etc. Note, however, that strings are a special case: str is iterable, but should be treated as a single value, not as a sequence of isolated characters.

An example of something that works in canvasapi 0.7.0:

course.list_multiple_submissions(student_ids='all', assignment_ids=[1, 2, 3])

Some examples of things that do not work in canvasapi 0.7.0:

# multiple values as a set
assignment_ids = set(...)
course.list_multiple_submissions(student_ids='all', assignment_ids=assignment_ids)

# multiple values as an iterator over dictionary values
mapping = {'a': 1, 'b': 2, 'c': 3}
course.list_multiple_submissions(student_ids='all', assignment_ids=mapping.values())

# multiple values as a generator expression
course.list_multiple_submissions(student_ids='all', assignment_ids=(assn.id for assn in ...))

Spoiler alert: I am already developing code to implement this feature. Expect a pull request soonish.

Submissions

Awkward to set context_codes[] parameter in Calendar.list_calendar_events call

The Canvas.list_calendar_events method feeds its parameters down to a GET /api/v1/calendar_events request. That request accepts a context_codes[] parameter, but setting that parameter correctly is surprisingly awkward in version 0.6.0 of canvasapi. The problem is that the parameter name really must be context_codes[], not context_codes, but creating a Python keyword argument with brackets in its name is subtle:

context = 'course_123456'

canvas.list_calendar_events(context_codes=context) 
# canvasapi.exceptions.CanvasException: API encountered an error processing your request

canvas.list_calendar_events(context_codes=[context])
# canvasapi.exceptions.CanvasException: API encountered an error processing your request

canvas.list_calendar_events(context_codes[]=context)
# SyntaxError: invalid syntax

canvas.list_calendar_events('context_codes[]'=context)
# SyntaxError: invalid syntax

canvas.list_calendar_events(**{'context_codes[]': context})
# success!

The syntactically invalid ones I knew were wrong before I even tried them. But for the first two attempts shown above, I guess I expected canvasapi to add the brackets for me either because it knew that context_codes[] was special, or perhaps it would always append brackets when I passed a value as a list ([context]) rather than a single item.

Arguably this is my fault: give the wrong parameter name, and of course things will fail. But I would have hoped that canvasapi would make this easier. The use of **{...} is obscure enough that many canvasapi users may not know how to do it at all.

Submission methods on wrong class

Was working with assignment submissions recently and discovered that many of the methods relating to submissions are in the wrong class. For example, list_submissionsappears in the Course and Section classes, but should probably just be in the Assignment class.

Need to move them to the appropriate class.

Use obj_or_id more widely to make APIs more flexible

canvasapi.util.obj_or_id makes it easier to implement flexible methods that accept either an int ID or an object that has an id attribute. This could be used more widely than it is right now. Just to give a few examples:

  • Canvas.get_course_nickname(self, course_id) could accept a Course object for course_id.

  • Section.get_submission(self, assignment_id, user_id, **kwargs) could accept an Assignment object for assignment_id, and could accept a User object for user_id.

  • Course.conversations_batch_update(self, conversation_ids, event) could accept a list of Conversation objects for conversation_ids. Note that allowing this would require applying obj_or_id to all values in a list rather than merely a single value.

Analytics

Support uploading files for submission comments

As far as I can tell, canvasapi 0.6.0 has no support for uploading files to be used in submission comments. I would like to automate parts of my grading workflow that require this capability. It would be nice for canvasapi to help me get that done.

Note that the complete file-upload workflow is rather complex. It would be great to have an elegant canvasapi interface that simplifies all of this. Barring that, access to each of the primitive steps would at least be better than nothing.

Python 3.x Support

Currently the library only works on Python 2.7. To be useful broader audience, we need to support Python 3.

Upgrading to Python 3 will probably also help resolve a lot of the Unicode errors we've been having.

Query paramaters and sis ids

Hi, i am new to this api but I like the implementations of it a lot.

A lot of the canvas api accepts query parameters,
Lets take for example listing users in a course where almost every parameter type is query

I would like to be able to add a query parameters for example
/api/v1/courses/{course_id}/users?enrollment_type=student
Is this possible now or how would you suggest the implementation?

Also, along with that it would also be handy to not use user_id but sis_user_id
and for course_id; sis_course_id.
However, sis_ids are not a query string. But instead come with a "sis_course_id:" in front of the sis id number itself.
example:
/api/v1/courses/sis_course_id:{{sis_course_id}}

Is that possible?, suggest implementation?

Currently I seem to be able to make a custom query like this
This example fetches only students in a course and I have tested it to be working.

	canvas = Canvas(API_URL, API_KEY)

	# First, retrieve the Course object
	course = canvas.get_course(COURSE_ID)

	
	'''You can make your own custom request like this'''
	from canvasapi.paginated_list import PaginatedList
	from canvasapi.user import User

	st_users = PaginatedList(User,
					course._requester,
					'GET',
					'courses/{}/users?enrollment_type=student'.format(COURSE_ID)
					,{})
	for st in st_users:
		print(st.id,st.name)

Page.edit fails, reporting that 'Page' object has no attribute 'id'

Using canvasapi 0.6.0, I have a Page object obtained by calling Course.get_page. I try to unpublish this page by calling page.edit(wiki_page={'published': 'false'}). (I use 'false' instead of False to avoid any possible additional instance of issue #64.)

Unfortunately, this Page.edit call fails:

  File "/home/liblit/.local/lib/python3.6/site-packages/canvasapi/page.py", line 33, in edit
    page_json.update({'course_id': self.id})
AttributeError: 'Page' object has no attribute 'id'

Printing dir(page) shows that while page indeed has no id attribute, it does have course_id, page_id, and parent_id attributes. Manually setting page.id = page.page_id before the Page.edit call does seem to fix the problem, though of course that is only a workaround.

Authentication Providers

return type of Course.get_full_discussion_topic()

I'm struggling to understand the return of get_full_discussion_topic(). I would greatly appreciate if someone would give an example of how you might parse the output? The same question applies to DiscussionTopic.list_topic_entries().

The endpoint GET /api/v1/courses/:course_id/discussion_topics/:topic_id/view natively returns a json string, which at least can be easily made into a dictionary. What can I do with the DiscussionTopic object type? Why was this chosen?

Thanks!

Course.list_multiple_submissions mismanages grouped response

The Course.list_multiple_submissions method returns a paginated list of Submission instances. However, printing any of these instances using its default string conversion fails:

submissions = course.list_multiple_submissions(student_ids='all', assignment_ids=(123, 456))
for submission in submissions:
    print(submission)

AttributeError: 'Submission' object has no attribute 'id'

If instead I print each submission’s __class__ and attribute dictionary, I see something rather strange. The class is indeed canvasapi.submission.Submission. However, the attributes are not those that a Submission should have. Ignoring the double-underscore internal attributes, each submission instance has:

  • _requester
  • attributes
  • integration_id
  • section_id
  • set_attributes
  • sis_user_id
  • submissions
  • to_json
  • user_id

The sis_user_id and user_id attributes tell me that this is some sort of person-identifying structure along the lines of a User or Enrollment object. But it does not have the complete set of attributes for either of those. The value of the submissions attribute is a list of dicts; I believe that each of these dicts corresponds to one Submission object.

The Course.list_multiple_submissions method produces a GET /api/v1/courses/:course_id/students/submissions request, and the documentation for that request shows that its response can take one of two forms depending on the grouped parameter. I can see that the Course.list_multiple_submissions implementation always hard-codes grouped=False, but the response coming back looks exactly like the Canvas API documentation example of a grouped response, complete with those submissions attributes giving lists of Submission objects.

So it seems that the grouped=False aspect of the request is not working as intended. I don’t know why; is this a Canvas bug or a canvasapi bug? Either way, the current behavior certainly is not working as intended. canvasapi should either request a non-grouped response in a way that works, or else it should process the grouped response in a way that respects the actual structure and meaning of the returned data.

For now I am working around this myself by building new Submission instances out of the dicts in each of the submissions attributes, like this:

groups = course.list_multiple_submissions(student_ids='all', assignment_ids=assignmentIds)
submissions = [Submission(course._requester, raw) for raw in chain.from_iterable(group.submissions for group in groups)]

But clearly that is not how the API is intended to be used.

Course.get_pages filtering on published status has no effect

Usung canvasapi 0.6.0, I call Course.get_pages to fetch a list of static web pages. If I pass the published=True keyword argument, the list should include only published pages. However, the list instead includes all pages, including unpublished ones. Conversely, using published=False should list only unpublished pages, but also returns a list of all pages.

Thinking that issue #64 could be manifesting here too, I also tried published='true' and published='false'. These also had no effect. It seems that the publication-status filtering option for this query has no effect whatsoever.

Add badges

It would be nice to have badges/shields to show the status of the project on the README.

Add any relevant badges/shields including Slack and Coveralls.

Grouped Responses

The list_multiple_submissions methods on Course and Section objects takes in an optional parameter grouped (See Canvas Docs). If grouped is true (note: see #61 for details on boolean weirdness for this particular method), the format Canvas returns is different than what PaginatedList normally expects.

To get around this, we prevent the grouped parameter from being passed at all. Ideally, we should see if it's possible to parse this other format and leverage it's benefit of being grouped by user.

Similar issues may exist in other endpoints. It's possible that support for different formats is related to #56.

Course.reorder_pinned_topics cannot be called correctly

In canvasapi 0.6.0, the Course.reorder_pinned_topics method has two defects that prevent it from being called correctly:

  1. When called with a list of discussion topic ids, this method performs a POST call with the topic ids given as repeated order parameters. For example, an order list of [1, 2, 3] turns into a POST request body of order=1,order=2,order=3. However, the documentation for the corresponding Canvas API method indicates that the ordering should actually be given as a single comma-delimited parameter: order=1,2,3.

  2. To work around the problem described above, one might consider turning the id list into a comma-delimited string before making the Course.reorder_pinned_topics call. For example, one might pass the order parameter to this call as the string "1,2,3" instead of the list [1, 2, 3]. However, the implementation of Course.reorder_pinned_topics uses isinstance(order, list) to check whether order is a list or not. If the argument is not a list, this method raises ValueError("Param order needs to be string or a list."). Although the error message explains that either a string or a list would be accepted, the code in question really allows only a list.

Taken together, these effectively prevent one from using canvasapi to reorder pinned discussions.

CanvasAPI Release v0.7.0

There is quite a bit of new and updated code on develop. It's time to ship this to production!

Figure out what currently uncompleted issues we'd like to prioritize for this release.

Create Conversation method not creating

Canvas.create_conversation() doesn't appear to be working properly.

If I try to create a conversation using the code bellow, nothing happens.

convo = canvas.create_conversation([USER_ID], 'Hello!')

The method returns a PaginatedList. However, if I iterate over the PaginatedList, the conversations are then created in Canvas. I suspect this is due to an odd interaction between PaginatedList and POST requests.

Additionally, in my testing, I was unable to successfully add a subject as a keyword argument:

convo = canvas.create_conversation([USER_ID], 'Hello!', subject='Test Subject')

The subject remained "(No Subject)"

Boolean flags mismanaged during assignment creation and update

Working with canvasapi 0.6.0, I recently used Course.create_assignment to create a large number of assignments. Each assignment had both peer_reviews and omit_from_final_grade explicitly set to False. (Granted, these are the defaults, but for unrelated reasons it was useful to be explicit.) I was surprised to find that the newly-generated courses had each of these settings turned on, rather than off.

I then used Assignment.edit to correct the bad settings, again with both peer_reviews and omit_from_final_grade explicitly set to False. The calls reported no errors, but these two settings remained True on subsequent queries or when viewed in the standard web interface.

Next I used Assignment.edit to correct the bad settings again, but this time with both peer_reviews and omit_from_final_grade explicitly set to 'false' as a lower-case string. The calls reported no errors, and the two settings were indeed changed as desired.

So it seems that False is either ignored or misinterpreted as true, whereas 'false' is interpreted as false. I can pass the 'false' string for now as a workaround, but surely it would be better for False to have the expected interpretation. When getting results back from the server, Boolean settings come back as True and False, not 'true' and 'false'. Passing settings up to the server in creation and modification requests should behave the same.

Canvas.list_calendar_events should accept datetime instances

The Canvas.list_calendar_events method feeds its parameters down to a GET /api/v1/calendar_events request. That request accepts start_date and end_date parameters, which “should be formatted as: yyyy-mm-dd or ISO 8601 YYYY-MM-DDTHH:MM:SSZ.” When using these parameters from Python, it would be convenient to provide values as Python datetime instances. For example, to list events between a course’ start and end dates:

from datetime import datetime, timedelta
soon = datetime.today() + timedelta(weeks=2)
events = canvas.list_calendar_events(end_date=soon)

This seems like an obvious way to make the call, but iterating over the result fails using version 0.6.0 of canvasapi:

canvasapi.exceptions.BadRequest: {'end_date': 'Invalid date or invalid datetime for end_date'}'}

The call works if I convert the date into a suitably-formatted (ISO 8601) string:

from datetime import datetime, timedelta
soon = datetime.today() + timedelta(weeks=2)
soon_iso = soon.isoformat()
events = canvas.list_calendar_events(end_date=soon_iso)

It would be very convenient if Canvas.list_calendar_events could accept parameters in the standard datetime a Python programmer would naturally expect to use, and would handle the ISO 8601 conversion internally.

Of course, dates and times appear in many other places in the Canvas API. I am reporting this against Canvas.list_calendar_events because that is where it is affecting me right now. But perhaps the best fix would be at a lower level within canvasapi so that every datetime instance is converted using .isoformat whenever given as the value of any request parameter. Is there a single, centralized place where that could be done?

getting-started.rst Canvas object's order of arguments is inconsistent

I'm just starting out with both Python and canvasapi (and very excited about what you all have shared here!), and was using your getting-started guide to get up and going. I noticed this line at the Usage section:

canvas = Canvas(API_URL, API_KEY)

Then a few lines down the page under Examples, the order of the arguments is switched:

canvas = Canvas(API_KEY, API_URL) (repeated twice in the Examples section)

This could be confusing for beginners like me.

Thanks again for sharing your work! Looking forward to digging into the functionality some more.

Project not showing up in Google searches

Hi,

Thanks for the great library!

I'm not sure how much you can do about this but you're not getting very good SEO on Google, which is a shame given the high quality of the library. The following terms do not give me a first page result for your Github project:

"canvas api python"
"canvas api"
"canvas lms api"
"canvas lms api client
"canvas lms api python"
"canvas lms api python client"

They at best just yield a couple of unmaintained libraries and I haven't been able to find any search terms that do yield your library except for "canvasapi" which isn't great for discoverability.

I've never had to deal with a problem like this before but maybe there's a way for you to update your README.md to improve your SEO visibility? I started rolling my own client library before I found your library by chance, and since then have spotted many other hastily written API clients and worry that this is causing an issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.