Различное поведение API Класса в работе при вызове индивидуально или из цикла - PullRequest
0 голосов
/ 09 мая 2020

Это сложная и странная проблема, с которой я столкнулся, и я надеюсь, что кто-нибудь сможет предложить мне, если не решение проблемы, что-нибудь, чтобы ее обойти. С самого начала я являюсь администратором школы, которая использует Google Suite for Education и Classroom для управления учебной работой со студентами, особенно сейчас, когда мы вынуждены работать из дома.

Мы думаем, что это должно быть Хорошая идея использовать Classroom API, чтобы получить хорошее представление о работе учащихся и их оценках в целом. Поэтому мы создали электронную таблицу и сценарий, чтобы получить отчет, в котором показаны все курсовые работы студента с его оценками по всем предметам (курсам в классе), которые он посещает. Пока что, после устранения некоторых недоразумений API, нам удалось закодировать скрипт.

После этого мы попытались увеличить размер скрипта, чтобы мы могли составить отчет для одной группы студентов за один раз, что является большой прогресс, так как составление 30 индивидуальных отчетов требует времени. Это было не очень сложно, так как у нас есть весь список студентов и групп в таблице, и более или менее это делается с помощью al oop. И это сработало, и мы можем получить полный отчет обо всех студентах в группе по всем их курсовым работам по всем предметам.

Но проблема возникла, когда мы начали запускать этот скрипт, и мы получаем ошибки в некоторых группы, у некоторых студентов, и мы не знаем почему. После некоторого расследования мы обнаружили, что вызов Classroom.Courses.list (optionalArgs) дает 430 курсов, хотя мы используем фильтр для студента в отчете. 430 курсов - это не большое число, но l oop для всех из них, даже с учетом того, что студент не зачислен на них, занимает больше 1800 секунд, и сценарий выдает «несвоевременную ошибку».

Удивительно, но если я запускаю ту же функцию, которая называется внутри l oop для группы, но при прямом вызове, то она работает безупречно и не вызывает ошибок. В этом случае студент, который выдает предыдущую ошибку, не зачислен ни на один курс, поэтому вызов Classroom.Courses.list (optionalArgs) дает мне нулевой набор курсов, и функция возвращается почти мгновенно без выполнения любой отчет.

Я показываю полный код скрипта, но интересная его часть не так уж и длинна. Он состоит из множества частей, которые выполняют вспомогательные задачи, такие как получение данных из других листов или отображение Html диалогов на экране.

С уважением, Рафаэль

PS Простите меня за этот длинный текст, но код намного длиннее, и я думаю, что объяснение ситуации может улучшить понимание нашей проблемы.

    // Menu
    function onOpen() {
      var ui = SpreadsheetApp.getUi();
      ui.createMenu('Functions')
          .addItem('Group report','fReportGroupHtml')
          .addItem('Student report','fStudentReportHtml')
          .addToUi();
    }

//------------------------------------------------------------------------
// Report for 1 student
//  Opens HTML dialog to select group and student
//------------------------------------------------------------------------
function fStudentReportHtml()
{
  // Html dialog
  try {
    var output = HtmlService.createHtmlOutputFromFile('SelecStudent');
    SpreadsheetApp.getUi().showModalDialog(output,'Select student');
  }
  catch(err) {
    Logger.log(err);
    return;
  }
}

//---------------------------------------------------------------------------
// Report for 1 student
//  Receives information from dialog and makes report
//------------------------------------------------------------------------
function fStudentReport(form_data)
{
  // Form information
  var nameStudent=form_data.student;

  // Split students name and surname
  var posco=nameStudent.indexOf(',');
  var surname=nameStudent.substring(0,posco);
  var name=nameStudent.substring(posco+2);

  // Gets student email
  var idStudent=getUserEmail(name,surname);

  // List student coursework
  var ssCourses=SpreadsheetApp.getActive();
  var hWorks=ssCourses.getSheetByName("StWork");
  fListStudentWork(idStudent,hWorks,nameStudent);
}

//---------------------------------------------------------------------------
// Report for all students in a group
//  Opens HTML dialog to select group
function fReportGroupHtml()
{
  // Html dialog
  try {
    var output = HtmlService.createHtmlOutputFromFile('SelecGroup');
    SpreadsheetApp.getUi().showModalDialog(output,'Select group');
  }
  catch(err) {
    Logger.log(err);
    return;
  }
}

//---------------------------------------------------------------------------
// Report for all students in a group
//  Receives information from dialog and makes report
function fReportGroup(form_data)
{
  var idStudents="1wvjqUFZcRwjTKSspjyQOE1JHio33f7seyx8xCO8qHBQ";
  var idTemplateInf="1K6YgZvh195eo4b-DLtRHuvjFdG3SOBJILodzS4CUnBs";
  var idCarpInf="16nH3BhAwHPP3BcAUK9eW_FQdWRM6B87T";
  var valAlu=[[]];

  // Opens students sheet
  var ss = SpreadsheetApp.openById(idStudents);
  var coursesSS = ss.getSheetByName("Students");
  var lastrow=coursesSS.getLastRow();
  var rangeA=coursesSS.getRange(2,2,lastrow-1,3).getValues();

  // First student in the group
  for(var ii=0; ii<lastrow-1; ii++)
  {
    if (rangeA[ii][2]==form_data.groupAlu)
      break;
  }
  var ini=ii;

  // Opens sheet for messaging
  var ssClass = SpreadsheetApp.getActive();

  // Copies template sheet into a new file
  var idNewFileInf = DriveApp.getFileById(idTemplateInf).makeCopy(idCarpInf).getId();
  DriveApp.getFileById(idNewFileInf).setName('Report '+form_data.groupAlu);

  // Opens new sheet and writes general information
  var ssInf = SpreadsheetApp.openById(idNewFileInf);
  var hjInfAlu = ssInf.getSheetByName("Students");
  hjInfAlu.getRange(2,1).setValue("GRUPO: "+form_data.groupAlu);
  var dateToday=new Date();
  hjInfAlu.getRange(3,1).setValue("FECHA: "+Utilities.formatDate(dateToday,"GMT+1","dd/MM/yyyy"));

  // All students in the group
  var iiAlu=1;
  while(rangeA[ii][2]==form_data.groupAlu)
  {
    // Gets student's information
    var surname=rangeA[ii][0];
    var name=rangeA[ii][1];
    var nameStudent=surname+", "+name;

    // Gets student email
    var idStudent=getUserEmail(name,surname);

    // Copies sheet
    var hjInfWork = ssInf.getSheetByName("Works").copyTo(ssInf).setName(nameStudent);

    // Makes report of student's work and submissions
    var mens="Creando informe de student: "+nameStudent;
    ssClass.toast(mens, "Informe de tareas",-1);
    var states=fListStudentWork(idStudent,hjInfWork,nameStudent);

    // Writes student information in first sheet
    valAlu[0][0]=iiAlu;
    valAlu[0][1]=nameStudent;
    valAlu[0][2]=states[0];
    valAlu[0][3]=states[1];
    valAlu[0][4]=states[2];
    hjInfAlu.getRange(iiAlu+5, 1, 1, 5).setValues(valAlu);
    ii++;
    iiAlu++;
  }

  // Deletes sheet 'Works'
  ssInf.deleteSheet(ssInf.getSheetByName("Works"));

  // Final message
  ssClass.toast("Fin del informe", "Informe de tareas",5);
}

//---------------------------------------------------------------------------
// List coursework for a student
function fListStudentWork(idStudent, dataSheet, nameStudent)
{
  // Variables
  var ii=0;
  var lWorks=[[]];
  var stateWork=[0,0,0];
  var pageToken = null;
  var pageToken2 = null;
  var colorCourse1= "#ddffdd";
  var colorCourse2= "#f9e9b0";
  var iiColor=0;
  var optionalArgs=
  {
    pageToken: pageToken,
    courseStates: 'ACTIVE',
    studentId: idStudent,
    pageSize: 0
  };
  var optionalArgs2=
  {
    userId: idStudent,
    pageSize: 0
  };

  // Date today
  var now = new Date();
  var year=now.getFullYear();
  var month=now.getMonth();

  // Date starting term
  if (month>=8) 
    var cadfecha="September 1, "+year.toString();
  else
    var cadfecha="September 1, "+(year-1).toString();  
  var fechaini=new Date(cadfecha);

  // Empty sheet
  var rowWorks=dataSheet.getLastRow();
  var colWorks=dataSheet.getLastColumn();
  if (rowWorks>3)
  {
    var rnWorks=dataSheet.getRange(4, 1, rowWorks-3, colWorks);
    rnWorks.clearContent().clearFormat();
  }

  // General information in sheet
  if (!nameStudent)
    nameStudent=getUserName(idStudent);
  Logger.log("INI: "+nameStudent);
  dataSheet.getRange(1, 1).setValue("ENTREGAS DEL ALUMNO: "+nameStudent);
  var dateToday=new Date();
  dataSheet.getRange(2, 1).setValue("FECHA: "+Utilities.formatDate(dateToday,"GMT+1","dd/MM/yyyy"));

  // First: courses for the student
  var response = Classroom.Courses.list(optionalArgs);
  var courses = response.courses;
  if (!courses || courses.length === 0)
    dataSheet.getRange(4, 2).setValue("No hay clases");
  else 
  {
    Logger.log(" courses: "+courses.length);
    for (course in courses)
    {
      var fechaCourse=new Date(courses[course].creationTime);

      if (fechaCourse>=fechaini)
      {
        // Information from the course
        var idCourse=courses[course].id;
        var nomprof=getUserName(courses[course].ownerId);
        var colorCourse=(iiColor==0)? colorCourse1 : colorCourse2;
        iiColor=1-iiColor;

        // Gets coursework from the course
        var responseT = Classroom.Courses.CourseWork.list(idCourse);
        var works = responseT.courseWork;
        if (!works || works.length === 0)
          dataSheet.getRange(4, 2).setValue("No hay información");
        else 
        {
          for (work in works)
          {
            var idWork=works[work].id;
            var maxPoints=(works[work].maxPoints==null)? "" : works[work].maxPoints;

            // Gets submissions
            try
            {
              var responseE = Classroom.Courses.CourseWork.StudentSubmissions.list(idCourse, idWork, optionalArgs2);
              var submis = responseE.studentSubmissions;
            }
            catch(ee)
            {
              var submis=null;
            }
            if (submis && submis.length >0)
            {
              for (subm in submis)
              {
                lWorks[0][0]=ii+1;
                lWorks[0][1]=courses[course].name;
                lWorks[0][2]=nomprof;
                lWorks[0][3]=works[work].title;
                var dateWork=works[work].dueDate;
                if (dateWork==null)
                  lWorks[0][4]="--/--/----";
                else 
                  lWorks[0][4]=dateWork.day+"/"+dateWork.month+"/"+dateWork.year;
                var points=(submis[subm].assignedGrade==null)? "" : submis[subm].assignedGrade; 
                lWorks[0][5]=points+" / "+maxPoints;
                var dateSubmis=submis[entrega].creationTime;
                if (dateSubmis==null)
                  lWorks[0][6]="--/--/----";
                else lWorks[0][6]=Utilities.formatDate(new Date(dateSubmis), "GMT+1","dd/MM/yyyy");
                var state=submis[subm].state;

                // Gets state of submission
                var cState="";
                var lateSubmis=0;
                if ((state=="RETURNED")||(state=="TURNED_IN"))
                {
                  cState="ENTREGADO";
                  stateWork[0]++;
                }
                else
                {
                  // Checks if late or not handed
                  if (dateWork!=null)
                  {
                    var dateEnd=new Date(dateWork.year,dateWork.month-1,dateWork.day);
                    if (dateEnd.valueOf()<dateToday.valueOf())
                    {
                      cState="NO ENTREGADO";
                      stateWork[1]++;
                      lateSubmis=1;
                    }
                    else
                      stateWork[2]++;
                  }
                }

                lWorks[0][7]=cState;              
                dataSheet.getRange(ii+4, 1, 1, 8).setValues(lWorks).setBackground(colorCourse);
                if (lateSubmis)
                  dataSheet.getRange(ii+4, 8).setFontColor("red").setFontWeight("bold");
                else
                  dataSheet.getRange(ii+4, 8).setFontColor("black").setFontWeight("normal");
                ii++;
              }
            }
          }  
        }
      }
    }
  }

  return(stateWork);
}

/////////////////////////////////////////////////////////////////////////////

//------------------------------------------------------------------------
// Gets list of groups
function listGroups()
{
  var idStudents="1wvjqUFZcRwjTKSspjyQOE1JHio33f7seyx8xCO8qHBQ";

  try 
  {
    // Opens students sheet
    var ss = SpreadsheetApp.openById(idStudents);
    var coursesSS = ss.getSheetByName("Students");
    var lastrow=coursesSS.getLastRow();
    var rangeA=coursesSS.getRange(2,4,lastrow-1,1).getValues();
    var courses = [];
    var courseant="";
    for(var ii=0; ii<lastrow-1; ii++) 
    {
      if (rangeA[ii][0]!=courseant)
      {
        courseant=rangeA[ii][0];
        courses.push(courseant);
      }
    }

    return courses;
  }
  catch(err) {
    Logger.log(err);
  }
}

//------------------------------------------------------------------------
// Gets list of students in group
function listStudents(group) 
{
  var idStudents="1wvjqUFZcRwjTKSspjyQOE1JHio33f7seyx8xCO8qHBQ";

  try 
  {
    // Opens students sheet
    var ss = SpreadsheetApp.openById(idStudents);
    var coursesSS = ss.getSheetByName("Students");
    var lastrow=coursesSS.getLastRow();
    var rangeA=coursesSS.getRange(2,1,lastrow-1,4).getValues();

    // First student
    for(var ii=0; ii<lastrow-1; ii++) 
    {
      if (rangeA[ii][3]==group)
        break;
    }
    var ini=ii;

    // Last student
    while(rangeA[ii][3]==group)
      ii++;

    // Gets array of students
    var students=coursesSS.getRange(2+ini,2,ii-ini,2).getValues();
    return students;
  }
  catch(err) {
    Logger.log(err);
  }
}

//------------------------------------------------------------------------
// Gets list of courses
function listCourses()
{
  try 
  {
    // Opens courses sheet
    var ss = SpreadsheetApp.getActive();
    var coursesSS = ss.getSheetByName("Courses");
    var lastrow=coursesSS.getLastRow();
    var courses=coursesSS.getRange(3,2,lastrow-2,4).getValues();
    return courses;
  }
  catch(err) {
    Logger.log(err);
  }
}

//---------------------------------------------------------------------------
// Gets user name from ID
function getUserName(usid)
{
  var result = AdminDirectory.Users.get(usid, {fields:'name'});
  var fullname = result.name.fullName;
  return fullname;
}

//----------------------------------------------------------------------------
// Gets user email from name
function getUserEmail(name,surname)
{
  var userIds = AdminDirectory.Users.list({domain:"iesciudadjardin.com",
                                           query:"givenName:'"+name+"' familyName:'"+surname+"'"}).users;
  if ((userIds==undefined)||(userIds.length!=1))
    return null;
  else
    return userIds[0].primaryEmail;
}

Дополнительная информация

Я понимаю, что вопрос слишком сложен, поэтому я попытаюсь объяснить вещи с минимальным количеством кода. По сути, мой код работает. Иногда это зависает с некоторыми учениками. Основная его часть - это функция fListStudentWork :

// List coursework for a student
function fListStudentWork(idStudent, dataSheet, nameStudent)
{
  var optionalArgs=
  {
    pageToken: pageToken,
    courseStates: 'ACTIVE',
    studentId: idStudent,
    pageSize: 0
  };

  // First: courses for the student
  var response = Classroom.Courses.list(optionalArgs);
  var courses = response.courses;
  if (!courses || courses.length === 0)
    // NO COURSES
  else 
  {
   // MAKE REPORT
  }
}

И теперь, если я вызываю ее с одним специальным учеником, она работает, возвращая нулевой набор курсов, поскольку Джон Смит не записан ни на один курс (это также случается со студентами, обучающимися на некоторых курсах):

function OneStudentReport()
{
  fListStudentWork("john.smith@mydomain.com",mySheet,"John Smith");
}

Но если я oop с набором студентов, в котором участвует Джон Смит, все предыдущие отчеты студентов в порядке, и когда l oop достигает его, он возвращает все курсы в моей школе, и сценарий прерывается с ошибкой тайм-аута.

function fReportGroup(form_data)
{
  // All students in the group
  while(StudentGroup=="MYGROUP")
  {
    // Gets student's information
    var surname=rangeA[ii][0];
    var name=rangeA[ii][1];
    var nameStudent=surname+", "+name;

    // Gets student email
    var idStudent=getUserEmail(name,surname);

    // Makes report of student's work and submissions
    var states=fListStudentWork(idStudent,MySheet,nameStudent);
  }
}

PS Если администратор считает, что я должен удалите первую часть сообщения с полным кодом, потому что он слишком длинный, дайте мне знать. Спасибо

1 Ответ

1 голос
/ 17 мая 2020

и извините за мой поздний ответ. Я тщательно тестировал свой код и думаю, что нашел источник ошибки. Функция fListStudentWork получает идентификатор студента, чтобы перечислить его работы. Этот идентификатор может быть его адресом электронной почты. Если я звонил напрямую, я использовал его электронную почту напрямую, поэтому ошибок не было. Однако, когда я использовал его в l oop для всей группы, я вспомнил его идентификатор из его имени и фамилии, и здесь я обнаружил некоторые ошибки: в нескольких sudent с тем же familyName и очень похожим givenName, который является как я получаю его электронную почту, используя Directory API. В этих нескольких случаях возвращенное электронное письмо было пустым, и функция fListStudentWork не проверяла наличие пустого электронного письма. Вдобавок внутри функции Classroom API вернул мне все 400 курсов, когда я запросил задания для нулевого студента, что в конечном итоге привело к тому, что мой скрипт отключился, так как не смог обработать такое количество курсов. Теперь вопрос может заключаться в том, почему Directory API возвращает null для таких пользователей, но я напишу это в другом вопросе. Спасибо всем за помощь.

...