Permalink
Browse files

Team member export and import feature (#1982)

* add export and import team feature

* add few lines regarding format of import csv on import team popup form

* add team member import/export feature. No change in database. New imported teams will have new team_id as if newly created by instructor or student

* allow teams import when empty teams on gradeable. no teams export if no non-null team
  • Loading branch information...
tushargr authored and bmcutler committed May 24, 2018
1 parent 2a79bce commit deafde544c23725880eb2a36887f4a8a812af518
@@ -23,6 +23,12 @@ public function run() {
case 'submit_team_form':
$this->adminTeamSubmit();
break;
case 'export_teams':
$this->exportTeams();
break;
case 'import_teams':
$this->importTeams();
break;
case 'grade':
$this->showGrading();
break;
@@ -383,7 +389,8 @@ public function showDetails() {
$rows = array_merge($rows, $individual_rows[""]);
}
}
$this->core->getOutput()->renderOutput(array('grading', 'ElectronicGrader'), 'detailsPage', $gradeable, $rows, $graders, $empty_teams);
$all_teams = $this->core->getQueries()->getTeamsByGradeableId($gradeable_id);
$this->core->getOutput()->renderOutput(array('grading', 'ElectronicGrader'), 'detailsPage', $gradeable, $rows, $graders, $all_teams, $empty_teams);
if ($gradeable->isTeamAssignment() && $this->core->getUser()->accessAdmin()) {
if ($gradeable->isGradeByRegistration()) {
@@ -398,9 +405,116 @@ public function showDetails() {
$all_sections[$i] = $section[$key];
}
$this->core->getOutput()->renderOutput(array('grading', 'ElectronicGrader'), 'adminTeamForm', $gradeable, $all_sections);
$this->core->getOutput()->renderOutput(array('grading', 'ElectronicGrader'), 'importTeamForm', $gradeable);
}
}
public function importTeams() {
$gradeable_id = (isset($_REQUEST['gradeable_id'])) ? $_REQUEST['gradeable_id'] : null;
$gradeable = $this->core->getQueries()->getGradeable($gradeable_id);
if ($gradeable == null) {
$this->core->addErrorMessage("Failed to load gradeable: {$gradeable_id}");
$this->core->redirect($return_url);
}
$return_url = $this->core->buildUrl(array('component'=>'grading', 'page'=>'electronic', 'action'=>'details','gradeable_id'=>$gradeable_id));
if (!$this->core->checkCsrfToken($_POST['csrf_token'])) {
$this->core->addErrorMessage("Invalid CSRF token");
$this->core->redirect($return_url);
}
if (!$gradeable->isTeamAssignment()) {
$this->core->addErrorMessage("{$gradeable->getName()} is not a team assignment");
$this->core->redirect($return_url);
}
if ($_FILES['upload_team']['name'] == "") {
$this->core->addErrorMessage("No input file specified");
$this->core->redirect($return_url);
}
$csv_file = $_FILES['upload_team']['tmp_name'];
register_shutdown_function(
function() use ($csv_file) {
unlink($csv_file);
}
);
ini_set("auto_detect_line_endings", true);
$contents = file($csv_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($contents === false) {
$this->core->addErrorMessage("File was not properly uploaded. Contact your sysadmin.");
$this->core->redirect($return_url);
}
$row_num=1;
$error_message="";
$new_teams_members = array();
foreach($contents as $content) {
$vals = str_getcsv($content);
$vals = array_map('trim', $vals);
if(count($vals) != 6) {
$error_message .= "ERROR on row {$row_num}, csv row do not follow specified format<br>";
continue;
}
if($row_num == 1) {
$row_num += 1;
continue;
}
$team_id = $vals[3];
$user_id = $vals[2];
if ($this->core->getQueries()->getUserById($user_id) === null) {
$error_message .= "ERROR on row {$row_num}, user_id doesn't exists<br>";
continue;
}
if(!array_key_exists($team_id, $new_teams_members)) {
$new_teams_members[$team_id] = array();
}
array_push($new_teams_members[$team_id], $user_id);
}
if($error_message != "") {
$this->core->addErrorMessage($error_message);
$this->core->redirect($return_url);
}
$gradeable_path = FileUtils::joinPaths($this->core->getConfig()->getCoursePath(), "submissions", $gradeable_id);
if (!FileUtils::createDir($gradeable_path)) {
$this->core->addErrorMEssage("Failed to make folder for this assignment");
$this->core->redirect($return_url);
}
foreach($new_teams_members as $team_id => $members) {
$leader_id = $members[0];
ElectronicGraderController::CreateTeamWithLeaderAndUsers($this->core, $gradeable, $leader_id, $members);
}
$this->core->addSuccessMessage("All Teams are imported to the gradeable");
$this->core->redirect($return_url);
}
public function exportTeams() {
$gradeable_id = $_REQUEST['gradeable_id'];
$all_teams = $this->core->getQueries()->getTeamsByGradeableId($gradeable_id);
$nl = "\n";
$csvdata="First Name,Last Name,User ID,Team ID,Team Registration Section,Team Rotating Section".$nl;
foreach ($all_teams as $team) {
if( $team->getSize() != 0) {
foreach(($team->getMembers()) as $member_id) {
$user = $this->core->getQueries()->getUserById($member_id);
$csvdata .= $user->getDisplayedFirstName().",".$user->getLastName().",".$member_id.",".$team->getId().",".$team->getRegistrationSection().",".$team->getRotatingSection().$nl;
}
}
}
$filename = "";
$filename = $this->core->getConfig()->getCourse()."_".$gradeable_id."_teams.csv";
$this->core->getOutput()->renderFile($csvdata, $filename);
return $csvdata;
}
public function adminTeamSubmit() {
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] != $this->core->getCsrfToken()) {
$this->core->addErrorMessage("Invalid CSRF Token");
@@ -1451,6 +1451,7 @@ public function declineAllTeamInvitations($g_id, $user_id) {
WHERE gt.g_id=? AND gt.team_id = t.team_id AND t.user_id=? AND t.state=0", array($g_id, $user_id));
}
/**
* Return Team object for team whith given Team ID
* @param string $team_id
@@ -329,7 +329,7 @@ public function statusPage(
* @param array $graders
* @return string
*/
public function detailsPage($gradeable, $rows, $graders, $empty_teams) {
public function detailsPage($gradeable, $rows, $graders, $all_teams, $empty_teams) {
$return = <<<HTML
<div class="content">
@@ -372,7 +372,22 @@ public function detailsPage($gradeable, $rows, $graders, $empty_teams) {
$show_auto_grading_points = true;
$return .= <<<HTML
<h2>Grade Details for {$gradeable->getName()}</h2>
<table class="table table-striped table-bordered persist-area">
HTML;
if ($gradeable->isTeamAssignment()) {
if(count($all_teams) > count($empty_teams)) {
$return .= <<<HTML
<a style="float: right;" class="btn btn-primary" href="{$this->core->buildUrl(array('component'=>'grading', 'page'=>'electronic', 'action'=>'export_teams', 'gradeable_id'=>$gradeable->getId()))}">Export Teams Members</a>
HTML;
}
if(count($all_teams) == count($empty_teams)) {
$return .= <<<HTML
<button style="float: right;" class="btn btn-primary" onclick="importTeamForm();">Import Teams Members</button>
HTML;
}
}
$return .= <<<HTML
<br /><br /><br /><table class="table table-striped table-bordered persist-area">
<thead class="persist-thead">
<tr>
HTML;
@@ -904,6 +919,32 @@ public function adminTeamForm($gradeable, $sections) {
return $return;
}
public function importTeamForm($gradeable) {
$return = <<<HTML
<div class="popup-form" id="import-team-form" style="width:550px; margin-left:-250px;">
<h2>Import Teams Members</h2>
<p>&emsp;</p>
<p>Format of the teams should be csv with 6 columns:<br />
First Name, Last Name, User ID, Team ID, Team Registration Section, Team Rotating Section<br />
The first row of the csv is assumed to be column headings and is ignored.<br /><br />
Note: Imported Teams will be assigned new Team IDs, Team Registration Section, and Team Rotating Section.
</p><br />
<form method="post" action="{$this->core->buildUrl(array('component'=>'grading', 'page'=>'electronic', 'action'=>'import_teams', 'gradeable_id'=>$gradeable->getId()))}" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="{$this->core->getCsrfToken()}" />
<div>
<input type="file" name="upload_team" accept=".csv">
</div>
<div style="float:right; width:auto;">
<a onclick="$('#import-team-form').css('display', 'none')" class="btn btn-danger">Cancel</a>
<input class="btn btn-primary" type="submit" value="Import">
</div>
</form>
</div>
HTML;
return $return;
}
//The student not in section variable indicates that an full access grader is viewing a student that is not in their
//assigned section. canViewWholeGradeable determines whether hidden testcases can be viewed.
public function hwGradingPage(Gradeable $gradeable, float $progress, string $prev_id, string $next_id, $studentNotInSection=false, $canViewWholeGradeable=false) {
@@ -692,7 +692,6 @@ ALTER TABLE ONLY sessions
ALTER TABLE ONLY users
ADD CONSTRAINT users_pkey PRIMARY KEY (user_id);


--
-- Name: gradeable_teams_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -973,7 +972,6 @@ ALTER TABLE ONLY users
ALTER TABLE ONLY gradeable_teams
ADD CONSTRAINT gradeable_teams_g_id_fkey FOREIGN KEY (g_id) REFERENCES gradeable(g_id) ON DELETE CASCADE;


--
-- Name: teams_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -407,6 +407,13 @@ function addCategory(old, i) {
});
}

function importTeamForm() {
$('.popup-form').css('display', 'none');
var form = $("#import-team-form");
form.css("display", "block");
$('[name="upload_team"]', form).val(null);
}

/**
* Toggles the page details box of the page, showing or not showing various information
* such as number of queries run, length of time for script execution, and other details

0 comments on commit deafde5

Please sign in to comment.