Trigger a function which updates Cloud Firestore when a student completes assignments or assignments are added for any course.
The official docs state that a feed for CourseWorkChangesInfo
requires a courseId
, and I would like to avoid having a registration and subscription for each course, each running on its own thread.
I have managed to get a registration to one course working:
def registration_body():
return { # An instruction to Classroom to send notifications from the `feed` to the
# provided destination.
"feed": { # Information about a `Feed` with a `feed_type` of `COURSE_WORK_CHANGES`.
"feedType": "COURSE_WORK_CHANGES", # Information about a `Feed` with a `feed_type` of `COURSE_WORK_CHANGES`.
"courseWorkChangesInfo": {
# This field must be specified if `feed_type` is `COURSE_WORK_CHANGES`.
"courseId": "xxxxxxxxxxxx", # The `course_id` of the course to subscribe to work changes for.
},
},
"cloudPubsubTopic": {
"topicName": "projects/xxxxx/topics/gcr-course", # The `name` field of a Cloud Pub/Sub
},
}
def create_registration(service):
"""
Creates a registration to the Google Classroom Service which will listen
for updates on Google Classroom according to the requested body.
Pub Sub will emit a payload to subscribers when a classroom upate occurs.
Args:
service (Google Classroom Service): Google Classroom Service as retrieved
from the Google Aclassroom Service builder
Returns:
Registration: Google Classroom Registration
"""
body = registration_body()
try:
registration = service.registrations().create(body=body).execute()
print(f"Registration to Google CLassroom Created\n{registration}")
return registration
except Exception as e:
print(e)
raise
And am successfully able to subscribe to those updates alongside my FastAPI server:
def init_subscription():
# [INIT PUBSUB SUBSCRIBER AND CALLBACKS]
subscriber = pubsub_v1.SubscriberClient()
subscription_path = subscriber.subscription_path(
"x-student-portal", "gcr-course-sub"
)
future = subscriber.subscribe(subscription_path, callback)
with subscriber:
try:
future.result()
except TimeoutError:
future.cancel()
future.result()
def callback(message):
print("message recieved")
# do_stuff(message)
print(message)
message.ack()
The registration and subscription initialization:
@app.on_event("startup")
async def startup_event():
global db
global gc_api
global gc_service
global gc_registration
global future
global pub_sub_subscription_thread
db = firestore.FirestoreDatabase()
gc_service = get_service()
gc_api = ClassroomApi(service=gc_service)
gc_registration = publisher_client.create_registration(gc_service)
pub_sub_subscription_thread = multiprocessing.Process(
target=publisher_client.init_subscription
)
pub_sub_subscription_thread.start()
I would very much like to avoid running multiple threads while still being able to subscribe to changes in all my courses.
Any advice would be appreciated.
This is not possible.
You cannot have a single registration to track course work changes for multiple courses, as you can see here:
Types of feeds
The Classroom API currently offers three types of feed:
- Each domain has a roster changes for domain feed, which exposes notifications when students and teachers join and leave courses in that domain.
- Each course has a roster changes for course feed, which exposes notifications when students and teachers join and leave courses in that course.
- Each course has a course work changes for course feed, which exposes notifications when any course work or student submission objects are created or modified in that course.
If you think this feature could be useful, I'd suggest you to file a feature request in Issue Tracker using this template.