Trying to use the StudentSubmissions.Patch part of the Classroom API in Google Apps Script and keeping running across this error
updateMask: updateMask may only contain "draftGrade" or
"assignedGrade"
Here is my code for that particular section:
var studentSubmission = {'draft_grade':'88'}
var patchC = Classroom.Courses.CourseWork.StudentSubmissions.patch(studentSubmission, courseId, cwId, submissionId);
There is clearly something wrong with the way I am passing the StudentSubmission Resource parameter, but I can't figure out why...
This is clearly the documentation I am referring to - https://developers.google.com/classroom/reference/rest/v1/courses.courseWork.studentSubmissions/patch
UPDATE
I was able to change the code a bit to reflect what you both were saying. Obviously, I didn't use exactly what you both said because KENdi's example is in Python and Ein2012, it would error out on the var patchC = Classroom... line.
I changed some things that now look like this:
var studentSubmission = {'draftGrade':'88'}
var extra = {'updateMask':'draftGrade'};
var patchC = Classroom.Courses.CourseWork.StudentSubmissions.patch(studentSubmission, courseId, cwId, submissionId, extra);
But now I get a different error "#ProjectPermissionDenied The Developer Console project is not permitted to make this request". So, now I'm unsure if that formatting is correct and there is some Developer Console situation I haven't resolved (although feel as though I'm correct), or that new formatting is wrong and I'm just getting the wild permission error.
I saw this Similar Error but what if the course work was one created normally through classroom and not via a script? Ahh.
specify update mask fields and later execute and also specify names as instructed in the documentation ("draftGrade","assignedGrade")
var studentSubmission = {'draftGrade':'88'}
var patchC = Classroom.Courses.CourseWork.StudentSubmissions.patch(studentSubmission, courseId, cwId, submissionId);
patchC.UpdateMask = "draftGrade";
var response = submisionObj.Execute();
From your error, it is stated that the updateMask must only contain "draftGrade" or "assignedGrade". So from your code, you need only that two value for the updateMask.
The updateMask identifies which fields on the student submission to update. This field is required to do an update. The update fails if invalid fields are specified
From this documentation, The StudentSubmission resource has two fields to store grades: assignedGrade, which is the grade reported to students, and draftGrade, which is a tentative grade visible only to teachers. These fields are updated using courses.courseWork.studentSubmissions.patch with a field mask containing the appropriate fields.
Here is the example code on how to do that.
studentSubmission = {
'assignedGrade': 99,
'draftGrade': 80
}
service.courses().courseWork().studentSubmission().patch(
courseId=<course ID or alias>,
courseWorkId=<courseWork ID>,
id=<studentSubmission ID>,
updateMask='assignedGrade,draftGrade',
body=studentSubmission).execute()
Related
I am trying to get the count of occurrences of a string in the text fetched from a website
var html = UrlFetchApp.fetch('https://www.larvalabs.com/cryptopunks/details/0000').getContentText();
var offers = html.match('Offered');
Logger.log(offers);
However I get the following data returned: [Offered]
I tried several methods but I do not find much documentation on those I can use to do this task that sounds simple.
I add that I tried to parse with XMLservice but some errors in the HTML code makes it fail.
For example, as one method, how about using matchAll()?
Modified script:
var html = UrlFetchApp.fetch('https://www.larvalabs.com/cryptopunks/details/0000').getContentText();
var offers = [...html.matchAll('Offered')]; // or [...html.matchAll(/Offered/g)]
Logger.log(offers.length);
When I tested above, 3 is returned.
Note:
In this case, the upper- and lowercase letters are distinguished. Please be careful this.
Reference:
matchAll()
I have adapted the script posted here slightly to suit my needs. It has worked very well for the most part searching through a list of over 4500 records and replacing based on a list of cross references 187 long.
function replMyText(){
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('summary');
var sh2=ss.getSheetByName('Dashboard');
var rgtxt=sh1.getRange('A7:A4934');//text to replace
var rgrep=sh2.getRange('K2:L188');//replacement table
var txtA=rgtxt.getValues();
var repA=rgrep.getValues();
for(var i=0;i<txtA.length;i++){
for(var j=0;j<repA.length;j++){
if(txtA[i][0]==repA[j][0]){
txtA[i][0]=repA[j][1];
}
}
}
rgtxt.setValues(txtA);
}
There are however 17 items that did not get replaced. The error I see on the script page is "Item already exists". I can't see any difference in those 17 values compared to the ones already replaced. Could anyone provide some guidance as to what the error is referring to ?
Edit: In response to the 1st comment, yes, it would have been handy to have a line number but there was no such thing given. Here is the screen grab of the message-
...and here is the xref of old item / new item. The list has been shortened as all of the successful replacements have been removed.
The execution transcript shows as follows -
Apologies for not supplying more data up front.
It turns out the issue is not with the script. The error being returned is actually a "referred" error from the worksheet where the values are being replaced. There was data validation set on that particular column to check for and try to prevent duplicate values from being entered. That was the error message that was being returned. When I suspended the data validation the script completed without error.
As mentioned in the comments below, if only students can turn something in, then I would need to be able to grade and return the assignment even though it had not been turned in yet. To clarify, the assignment was made with the API and I have control over the class and the student.
As mentioned in additional comments below, even if you do not return the assignment, the student will still see the assigned grade, which fulfills the goal of this question. Thus, turn-ins and returns are not needed to grade assignments with the Classroom API.
There are a couple of questions similar to this one, but not as complete and none seem to have concrete answers, so I will try and be as specific as possible. I need to be able to force-turn-in a student's assignment, even though they have not done so yet, and then put a grade, and then return it. With the script below, I am getting two errors.
One, "Invalid JSON payload received. Unknown name "assignment_submission": Cannot find field. (line 15, file "GRADES CLASSROOM")" even though this is in the Classroom reference (https://developers.google.com/classroom/reference/rest/v1/courses.courseWork.studentSubmissions).
Two, when I try and use the "return" method, (second last line of code) it returns the error "Missing name after . operator. (line 17, file "GRADES CLASSROOM")" when I try and save the code, forcing me to comment it out before saving. I think many people have been looking for an answer to how to set this process up.
function returnGrade () {
var submit ={assignedGrade: 80};
var upDate = {updateMask: 'assignedGrade'};
var resource1 = {
assignmentSubmission: {
addAttachments: [{
link:{
url: "URL"
},
}],
},
};
// Classroom.Courses.CourseWork.StudentSubmissions.turnIn(resource1, COURSE ID, WORK ID, "EMAIL");
Classroom.Courses.CourseWork.StudentSubmissions.patch(submit, COURSE ID, WORK ID, "EMAIL", upDate);
// Classroom.Courses.CourseWork.StudentSubmissions.return(resource1, COURSE ID, WORK ID, "EMAIL");
}
In the comments below, the code to set the grade has been resolved. We only need to see how to return the grade.
Short answer
Course work submissions (assignments, questions) could only be done by students or by using their credentials. We could uso OAuth for this but if you don't want to use OAuth, then turn in the course work by using the student account, in other words, make a function to be ran by with the student account and make another function to do the the patch / return operations with the teacher / domain administrator account.
It's possible to grade student course work submissions even if they aren't submitted by using patch.
Course work submissions can only be returned to students if they were turned if first
Code
The following code added the grades to one student "successfully". At this time I was able to verify that drafGrade was correctly registered on the web UI.
function doPatch(){
var courseId = '6915813348';
var courseWorkId = '11297534926';
var studentSubmissions = Classroom.Courses.CourseWork.StudentSubmissions.list(courseId, courseWorkId,{'userId':'someone#gmail.com'});
var id = studentSubmissions['studentSubmissions'][0].id;
var studentSubmission = {
'assignedGrade': 99,
'draftGrade': 80
}
Classroom.Courses.CourseWork.StudentSubmissions.patch(
studentSubmission,
courseId,
courseWorkId,
id,
{
'updateMask':'assignedGrade,draftGrade'
}
);
}
The assignedGrade is visible to the teacher and student on the student work view
The draftGrade is visible to the teacher on the submissions view
Notes
I made some research and tests and found some "interesting things"
The error message
"Missing name after . operator. (line 17, file "GRADES CLASSROOM")"
Looks to be caused by the use of return in
Classroom.Courses.CourseWork.StudentSubmissions.return(resource1, COURSE ID, WORK ID, "EMAIL");
Reference: Google App Script
The error occurs because return is a JavaScript keyword. The alternative syntax is
Classroom.Courses.CourseWork.StudentSubmissions["return"](resource1, COURSE ID, WORK ID, "EMAIL")
But this requires that the related student submitted the course work otherwise the Classroom API will throw a Failed Precondition error.
I don't have a complete answer yet, but it's important to bear in mind that some tasks depend on the user class role that could be teacher or student but not both for the same course.
So in order to make an assignment submission the corresponding method should be called someway on behalf the corresponding student and in order to return an assignment this should be called someway on behalf a class teacher.
References
From Method: courses.courseWork.studentSubmissions.turnIn
This may only be called by the student that owns the specified student submission.
From Method: courses.courseWork.studentSubmiss.return
Only a teacher of the course that contains the requested student submission may call this method.
I am trying to create a SearchFolder using the EWS API (managed or web service directly). I noticed that I if I create a SearchFilter.ContainsSubstring on the ItemSchema.Body, I do not get any conversations from it.
here is how I create my folder:
var folder = new SearchFolder(service)
{
DisplayName = topic
};
var searchParameters = folder.SearchParameters;
searchParameters.SearchFilter = new SearchFilter.ContainsSubstring(ItemSchema.Body, topic, ContainmentMode.Substring, ComparisonMode.IgnoreCaseAndNonSpacingCharacters);
searchParameters.RootFolderIds.Add(WellKnownFolderName.Root);
searchParameters.Traversal = SearchFolderTraversal.Deep;
folder.Save(WellKnownFolderName.SearchFolders);
Later, I try to get the conversations from this folder:
service.FindConversation(conversationView, folder.Id);
And this returns 0 conversations.
I made sure by sending two messages to my email account, the first with a special term only in the subject, and the second with the same term in the body. If I create a SearchFolder with a filter on the ItemSchema.Subject, I get the first conversation, but using the SearchFolder I created above, I do not get the expected result.
Are there some restrictions regarding the ContainsSubstring SearchFilter? I tried using NormalizedBody or TextBody, but then I got errors in the folder creation process. Is there anything else I am missing?
Doing a search filter on the body will likely be problematic. This goes back to how potentially large properties like Body are handled in contents tables. A query string search would likely work better, but you can't use a query string to create a search folder.
I need to retrieve calendar information by invoking the Exchange Web Service in BPOS. I'm using a CalendarView with a PropertySet to retrieve as little data as possible. However, property sets seems to be limited. I need the EmailAddress of the one who made the calendar appointment so I thought I could use the AppointmentSchema.Organizer in the PropertySet.
When fetching a whole appointment I can get the e-mail through appt.Organizer.EmailAddress. But with the code below the Organizer.EmailAddress is always null. I've enabled the trace and checked it and only the Organizer.Name property is sent, nothing else. Does anyone have a solution on how to get the EmailAddress when using a PropertySet?
CalendarView view = new CalendarView(dtFrom, dtTo);
view.PropertySet = new PropertySet(ItemSchema.Subject);
view.PropertySet.Add(ItemSchema.Id);
view.PropertySet.Add(AppointmentSchema.Start);
view.PropertySet.Add(AppointmentSchema.End);
view.PropertySet.Add(AppointmentSchema.Organizer); // This should contain EmailAddress but it doesn't
Mailbox mailbox = new Mailbox("myemail#test.ab");
FolderId id = new FolderId(WellKnownFolderName.Calendar, mailbox);
CalendarFolder folder = CalendarFolder.Bind(service, id);
FindItemsResults<Appointment> findResults = folder.FindAppointments(view);
This should work (does for me):
service.FindAppointments(WellKnownFolderName.Calendar, new CalendarView(start, end)).Where(s => DateTime.Now < s.Start);
service.LoadPropertiesForItems(appointments, PropertySet.FirstClassProperties);
As best as I have been able to figure out EWS is a little buggy when it comes to populating the full EmailAddress details both in Appointments for Organizer and for other things like "EmailMessage.From". When you do a query for multiple items, you don't get the EmailAddress properties being fully populated. E.g. using APIs like:
Folder.FindItems
ExchangeService.FindAppointments
I find that only the display name in the EmailAddress fields gets populated.
To get the EmailAddress fully populated I find I need to load/bind to the specific item and specify the relevant EmailAddress property, e.g. AppointmentSchema.Organizer in your case. So although you specify exactly the same property to load, you are loading using a single item call rather than a bulk query. E.g. using:
ServiceObject.Load
Which is available for both Appointment and EmailMessage as they both derive off ServiceObject. Using Item.Bind with the appropriate property set defined should also work.
As an aside I figured this out looking at the code for EwsEditor which is mentioned here:
http://blogs.msdn.com/webdav_101/archive/2009/11/10/ews-has-more-happy-now-ews-managed-api-and-ewseditor.aspx
The usability of EwsEditor is fairly sucky, and the code takes some trawling to figure out, but at least it does show examples of exercising many of the APIs.
service.FindAppointments(WellKnownFolderName.Calendar, new CalendarView(start, end)).Where(s => DateTime.Now < s.Start);
service.LoadPropertiesForItems(appointments, PropertySet.FirstClassProperties);
It worked for me.