Giter VIP home page Giter VIP logo

piazza-api's Introduction

hfaran.github.io Content

Publishing Hugo site to hfaran.github.io

  • Adapted from Host on Github article on Hugo documentation
  • Run hugo in the hugo project folder (hfaran.github.io\)
    • This will generate content into the public folder which is a submodule to the hfaran.github.io repository. Commit and push changes in that repository to deploy to hfaran.github.io

piazza-api's People

Contributors

aparande avatar arxanas avatar asinghani avatar ausaf-a avatar blx avatar camerondurham avatar davidsnider avatar developstorm avatar egregori3 avatar evanj3 avatar hfaran avatar icamacho3 avatar j-beastman avatar jlumbroso avatar kavigupta avatar keagan-piazza avatar kk1423 avatar nacgarg avatar savantes1 avatar skystrife avatar vishhvak 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

piazza-api's Issues

Update post not working with new piazza api

So far I've been able to get posts to create, but not update. Since a semi-recent update to their backend, I've had to monkeypatch the create_post piazza api seen below for working example:

# login to piazza with a specific class
# all class specifics should be given either
# in an .env file (development) or environment
# variables 
piazza_access = Piazza()

piazza_access.user_login(
    email=login_credentials.email,
    password=login_credentials.password.get_secret_value(),
)

# login to that specific course.
course_logged = piazza_access.network(network_id=login_credentials.course_id)

def create_post(
    self,
    post_type,
    post_folders,
    post_subject,
    post_content,
    visible_to,
    is_announcement=0,
    bypass_email=0,
    anonymous=False,
):
    """Create a post

    It seems like if the post has `<p>` tags, then it's treated as HTML,
    but is treated as text otherwise. You'll want to provide `content`
    accordingly.

    :type post_type: str
    :param post_type: 'note', 'question'
    :type post_folders: str
    :param post_folders: Folder to put post into
    :type post_subject: str
    :param post_subject: Subject string
    :type post_content: str
    :param post_content: Content string
    :type visible_to: str
    :param visible_to: Users this post is visible to
    :type is_announcement: bool
    :param is_announcement:
    :type bypass_email: bool
    :param bypass_email:
    :type anonymous: bool
    :param anonymous:
    :rtype: dict
    :returns: Dictionary with information about the created post.
    """
    params = {
        "anonymous": "yes" if anonymous else "no",
        "subject": post_subject,
        "content": post_content,
        "folders": post_folders,
        "type": post_type,
        "config": {
            "bypass_email": bypass_email,
            "is_announcement": is_announcement,
            "feed_groups": visible_to,
        },
    }

    return self._rpc.content_create(params)

course.create_post = types.MethodType(monkeypatch.create_post, course)

#### This works just fine!!!
course.create_post(
            **CreatePostModel(  # pydantic model with needed info
                post_type="note",
                post_folders=["other"],
                visible_to=posted_to,
                post_content=lecture_content_html,
                post_subject="Lecture Slides",
            ).model_dump()

However, no luck for updating posts, anything similar like feeding in cid (post id) and post content, and any permutation of the additional info needed for the create monkeypatch, gives the same response:

def update_post_monkeypatch(
    self,
    cid: int,
    post_type: str,
    post_folders: str,
    post_subject: str,
    post_content: str,
    visible_to: str,
    is_announcement: int = 0,
    bypass_email: int = 1,
    anonymous: bool = False,
):
    """Update a post, the **updated** way!

    It seems like if the post has `<p>` tags, then it's treated as HTML,
    but is treated as text otherwise. You'll want to provide `content`
    accordingly.

    :type post_type: str
    :param post_type: 'note', 'question'
    :type post_folders: str
    :param post_folders: Folder to put post into
    :type post_subject: str
    :param post_subject: Subject string
    :type post_content: str
    :param post_content: Content string
    :type visible_to: str
    :param visible_to: Users this post is visible to
    :type is_announcement: bool
    :param is_announcement:
    :type bypass_email: bool
    :param bypass_email:
    :type anonymous: bool
    :param anonymous:
    :rtype: dict
    :returns: Dictionary with information about the created post.
    """
    # Tried any permutation starting with simplest (cid and content) and iterated, but no luck
    params = {
        "cid": cid,
        "anonymous": "yes" if anonymous else "no",
        "subject": post_subject,
        "content": post_content,
        "folders": post_folders,
        "type": post_type,
        "config": {
            "bypass_email": bypass_email,
            "is_announcement": is_announcement,
            "feed_groups": visible_to,
        },
    }

    return self._rpc.content_update(params)

course.update_post = types.MethodType(update_post_monkeypatch, course)

course.update_post(
            **UpdatePostModel(
                cid=post_id,
                post_type="note",
                post_folders=["other"],
                # visible_to=posted_to,
                post_content=lecture_content_html,
                post_subject="Lecture Slides",
            ).model_dump()
        )

Something to do with this url made to piazza's backend: https://piazza.com/logic/api?method=content.update&aid=<aid-nonce_here>
Response: {\n "result": null,\n "error_codes": [],\n "error": "The post you are looking for cannot be found",\n "aid": "luxl22pj6p74en"\n}

I've tried every permutation or including or excluding things, and haven't been able to make any headway, so I'm a bit stumped! Any ideas? Happy to do some work on this if someone knows a fix.

Investigate reverse-engineering API further

Excerpt from email:

Existing Piazza API Documentation

Moving onto your second question. Unfortunately, I do not think any such documentation exists. Piazza's REST API seems to be internal-only and is not publicized anywhere. I personally only found about it from this gist. Unless Piazza is interested in making their API public, I believe the only way to find out more about Piazza's API would be to reverse-engineer it. I have not attempted to do this, so I am not sure how difficult it would be, but I am intrigued and may look into this later. It may also be worth contacting alexjlockwood about how he came across the little bit of the API that he posted about in his gist.

If you have further questions, I'd be happy to keep this thread going.


Could probably use something like Fiddler (maybe Firebug) for this?

get_all_users( ) not working

Hi,
Testing out your api and it's nice. Thanks for making it pretty straightforward.

I've got an issue with get_all_users returning the error below:

RequestError: Could not get users.
Response: {
"aid": "iduiwc98geh5e0",
"error_codes": [],
"result": null,
"error": "Do not have permission"
}

I'm a student of one of the classes I was querying. I was wondering if the get_all_users( ) function is only open to instructors.

Thanks

Unable to login with p.user_login()

Here's the traceback:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/johneastman/.pyenv/versions/TABot/lib/python3.10/site-packages/piazza_api/piazza.py", line 22, in user_login
    self._rpc_api.user_login(email=email, password=password)
  File "/Users/johneastman/.pyenv/versions/TABot/lib/python3.10/site-packages/piazza_api/rpc.py", line 78, in user_login
    raise AuthenticationError("Could not authenticate.\n{}"
piazza_api.exceptions.AuthenticationError: Could not authenticate.
{'result': None, 'error_codes': [], 'error': 'Request not valid', 'aid': 'llqqes5ni9170'}

I'm positive that the credentials are correct.

login failing "Email or password incorrect"

However, I have confirmed 5 ways from sunday that the email and password are correct. I experimented with adding urlencoding to the user and pass but that does not seem to be helping. Any ideas? Would really like to be able to use your library to script some of my piazza posts.

ImportError: no module named 'requests'

When trying to install piazza-api in an empty virtualenv, this error comes up:

$ virtualenv -p python3 .venv
$ source .venv/bin/activate
$ pip install piazza-api
Downloading/unpacking piazza-api
  Downloading piazza-api-0.5.0.tar.gz
  Running setup.py (path:/Volumes/Home/Users/Waleed/tmp/python/piazza-api-test/.venv/build/piazza-api/setup.py) egg_info for package piazza-api
    Traceback (most recent call last):
      File "<string>", line 17, in <module>
      File "/Volumes/Home/Users/Waleed/tmp/python/piazza-api-test/.venv/build/piazza-api/setup.py", line 7, in <module>
        import piazza_api
      File "/Volumes/Home/Users/Waleed/tmp/python/piazza-api-test/.venv/build/piazza-api/piazza_api/__init__.py", line 1, in <module>
        from piazza_api.piazza import Piazza
      File "/Volumes/Home/Users/Waleed/tmp/python/piazza-api-test/.venv/build/piazza-api/piazza_api/piazza.py", line 1, in <module>
        from .rpc import PiazzaRPC
      File "/Volumes/Home/Users/Waleed/tmp/python/piazza-api-test/.venv/build/piazza-api/piazza_api/rpc.py", line 4, in <module>
        import requests
    ImportError: No module named 'requests'
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):

  File "<string>", line 17, in <module>

  File "/Volumes/Home/Users/Waleed/tmp/python/piazza-api-test/.venv/build/piazza-api/setup.py", line 7, in <module>

    import piazza_api

  File "/Volumes/Home/Users/Waleed/tmp/python/piazza-api-test/.venv/build/piazza-api/piazza_api/__init__.py", line 1, in <module>

    from piazza_api.piazza import Piazza

  File "/Volumes/Home/Users/Waleed/tmp/python/piazza-api-test/.venv/build/piazza-api/piazza_api/piazza.py", line 1, in <module>

    from .rpc import PiazzaRPC

  File "/Volumes/Home/Users/Waleed/tmp/python/piazza-api-test/.venv/build/piazza-api/piazza_api/rpc.py", line 4, in <module>

    import requests

ImportError: No module named 'requests'

----------------------------------------
Cleaning up...
Command python setup.py egg_info failed with error code 1 in /Volumes/Home/Users/Waleed/tmp/python/piazza-api-test/.venv/build/piazza-api
Storing debug log for failure in /Users/Waleed/.pip/pip.log

The error is fixed by manually installing requests and then installing piazza-api. Do you know how to fix this issue? (Possibly by specifying a specific version of requests in requirements.txt?)

Tests with responses

Testing against the actual Piazza SaaS is likely more trouble than it's worth; such tests could be set up to tell us when piazza-api breaks against any future changes to the Piazza API, although.

That being said, testing against a static mock API using responses seems like a much better idea for the time being. This won't tell us when Piazza API changes break piazza-api against Piazza, but it will tell us when bad refactoring breaks piazza-api against the mockup.

Figure out how to use the network apis

The network apis do not currently work with this mechanism. It seems they need a different session key. The most important network api in my opinion: network.get_my_feed. This allows the program to get all of the CIDs necessary to get content.

Getting posts raises a RequestError

Calling network.iter_all_posts() or network.get_post() fails and raises a request error. The inner error returned from the Piazza API appears to be a very unhelpful "request not valid".

I suspect this is because the API has been changed between now and when this was last updated.

Allow authentication with demo users

Excerpt from email:

Authenticating with Demo User

So I just looked into this, and to answer your first question, this is easy enough; all we need to do is grab the cookies that we get for the demo user and then we can use the API as per usual.

In [1]: import requests

In [2]: r = requests.get("https://piazza.com/demo_login?nid=hbj26a6gcvl1s6&auth=06c287b")

In [3]: r.cookies
Out[3]: <<class 'requests.cookies.RequestsCookieJar'>[Cookie(version=0, name='piazza_session', value='ttxyDKEyKuGFEKJLwxEtDxxJJHLECwuH.5%27tz5M55%3F5%28%26x%255M5%23%28u%7F%7Cvr%7Bu%7DEItIzv%29%7FD%26I5%3F5%21%7Cw%265M5%7Bu%7DEItIzv%29%7FD%26IMC5%3F5%275MD%3F5%26x%26%26%7C%22%21r%7Cw5M5Vrab_r%5DjkZ+w5%3F5%2A%7Bx%215MDGCGEIKLCJ%3F5%23x%25+5MC0', port=None, port_specified=False, domain='piazza.com', domain_specified=False, domain_initial_dot=False, path='/', path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={'HttpOnly': None}, rfc2109=False)]>

You can see above that if we hit the demo login URL, the response contains cookies that we can use. Further, I'll just use the given cookie to get some arbitrary post from your CS 422 group on Piazza. Most of the following code is from the get method from my PiazzaAPI class.

In [4]: nid = "hbj26a6gcvl1s6"  # This is the 'nid' from the URL of your class page

In [5]: content_url = 'https://piazza.com/logic/api'

In [6]: cid = "249"  # This is just the ID of an arbitrary post I picked out

In [7]: content_data = {
            "method": "content.get",
            "params": {
                "cid": cid,
                "nid": nid
            }
        }

In [9]: import json

In [11]: content_params = {"method": "get.content"}

In [13]: res = requests.post(
   ....:             content_url,
   ....:             data=json.dumps(content_data),
   ....:             params=content_params,
   ....:             cookies=r.cookies)  # As you can see, here I am using the cookies that we got before

In [14]: res.json()
Out[14]:
{u'aid': u'hx41mlbnsi2z0',
 u'error': None,
 u'result': {u'bookmarked': 1,
  u'bucket_name': u'Today',
  u'bucket_order': 2,
  u'change_log': [{u'anon': u'no',
    u'data': u'hgzgfgxw1x42jr',
    u'type': u'create',
    u'uid': u'gs0h4ststh2',
    u'when': u'2013-05-21T19:06:58Z'}],
  u'children': [],
  u'config': {},
  u'created': u'2013-05-21T19:06:58Z',
  u'data': {u'embed_links': []},
  u'default_anonymity': u'no',
  u'folders': [u'project3'],
  u'history': [{u'anon': u'no',
    u'content': u'Grades for Project 3 are up on the grade server now. If you want details on how your grade was calculated (auto-grading output, writeup notes, etc.), please send me the UID of the team member that submitted your project.\n\nAlso, don&#39;t panic if you submitted something but have a zero on the grade server.  I suspect that I didn&#39;t actually receive all of the P3 submissions but I&#39;m sure that they&#39;re somewhere.  Just let me know if you submitted something but still got a zero and we&#39;ll sort it out.\n\nFinally, as usual, you have one week to lodge any complaints you may have about your grade.',
    u'created': u'2013-05-21T19:06:58Z',
    u'subject': u'Project 3 Grades Are Up',
    u'uid': u'gs0h4ststh2'}],
  u'i_edits': [],
  u'id': u'hgzgfgxtvqk2jq',
  u'is_bookmarked': False,
  u'is_tag_good': False,
  u'my_favorite': False,
  u'no_answer_followup': 0,
  u'nr': 249,
  u'num_favorites': 0,
  u'q_edits': [],
  u'request_instructor': 0,
  u'request_instructor_me': False,
  u's_edits': [],
  u'status': u'active',
  u't': 1404269205107L,
  u'tag_good': [],
  u'tag_good_arr': [],
  u'tags': [u'instructor-note', u'project3'],
  u'type': u'note',
  u'unique_views': 36,
  u'upvote_ids': []}}

And there we have the content from a post. :)

Allowing authentication using demo users through demo URLs such as above should be fairly easy to bake into my project. I will attempt to do this sometime this week. You can also submit a pull request if you would like to do it.

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.