Groundwork for pages and periods in backend
This commit is contained in:
parent
ff321e21c5
commit
3687461585
|
@ -195,7 +195,7 @@ class gradeinfo {
|
|||
"required" => $this->is_required(),
|
||||
];
|
||||
// Unfortunately, lazy loading of the completion data is off, since we need the data to show study item completion...
|
||||
if($studyitem !== null && $this->is_selected() && has_capability('local/treestudyplan:viewuserreports',$studyitem->getStudyline()->getStudyplan()->context())
|
||||
if($studyitem !== null && $this->is_selected() && has_capability('local/treestudyplan:viewuserreports',$studyitem->getStudyline()->studyplan()->context())
|
||||
&& $this->gradingscanner->is_available()){
|
||||
$model['grading'] = $this->gradingscanner->model();
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ class gradeinfo {
|
|||
if(!isset($this->studyitem)){
|
||||
throw new \UnexpectedValueException("Study item not set (null) for gradeinfo in report mode");
|
||||
}
|
||||
$aggregator = $this->studyitem->getStudyline()->getStudyplan()->getAggregator();
|
||||
$aggregator = $this->studyitem->studyline()->studyplan()->aggregator();
|
||||
$completion = $aggregator->grade_completion($this,$userid);
|
||||
|
||||
$model = [
|
||||
|
|
|
@ -72,7 +72,7 @@ class tristate_aggregator extends \local_treestudyplan\aggregator {
|
|||
}
|
||||
|
||||
public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid){
|
||||
$condition = $studyitem->getConditions();
|
||||
$condition = $studyitem->conditions();
|
||||
if(empty($condition)){
|
||||
$condition = self::DEFAULT_CONDITION;
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ class tristate_aggregator extends \local_treestudyplan\aggregator {
|
|||
}
|
||||
|
||||
public function aggregate_junction(array $completion, studyitem $studyitem, $userid){
|
||||
$completed = self::aggregate_completion($completion,$studyitem->getConditions());
|
||||
$completed = self::aggregate_completion($completion,$studyitem->conditions());
|
||||
// if null result (conditions are unknown/null) - default to ALL
|
||||
return isset($completed)?$completed:(self::aggregate_completion($completion,'ALL'));
|
||||
}
|
||||
|
|
|
@ -27,11 +27,11 @@ class studyitem {
|
|||
return $this->studyline->context();
|
||||
}
|
||||
|
||||
public function getStudyline(): studyline {
|
||||
public function studyline(): studyline {
|
||||
return $this->studyline;
|
||||
}
|
||||
|
||||
public function getConditions() {
|
||||
public function conditions() {
|
||||
return $this->r->conditions;
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ class studyitem {
|
|||
$this->r = $DB->get_record(self::TABLE,['id' => $id],"*",MUST_EXIST);
|
||||
|
||||
$this->studyline = studyline::findById($this->r->line_id);
|
||||
$this->aggregator = $this->getStudyline()->getStudyplan()->getAggregator();
|
||||
$this->aggregator = $this->studyline()->studyplan()->aggregator();
|
||||
}
|
||||
|
||||
public function id(){
|
||||
|
@ -147,7 +147,7 @@ class studyitem {
|
|||
} else {
|
||||
// Also supply a list of linked users, so the badgeinfo can give stats on
|
||||
// the amount issued, related to this studyplan
|
||||
$studentids = $this->getStudyline()->getStudyplan()->find_linked_userids();
|
||||
$studentids = $this->studyline()->studyplan()->find_linked_userids();
|
||||
$model['badge'] = $badgeinfo->editor_model($studentids);
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ class studyitem {
|
|||
$item = self::findById($id);
|
||||
if($item->type() == self::COURSE){
|
||||
// Signal the studyplan that a course has been added so it can be marked for csync cascading
|
||||
$item->getStudyline()->getStudyplan()->mark_csync_changed();
|
||||
$item->studyline()->studyplan()->mark_csync_changed();
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
|
|
@ -26,13 +26,14 @@ class studyline {
|
|||
|
||||
private $r; // Holds database record
|
||||
private $id;
|
||||
private $page;
|
||||
private $studyplan;
|
||||
|
||||
public function context(): \context {
|
||||
return $this->studyplan->context();
|
||||
}
|
||||
|
||||
public function getStudyplan() : studyplan {
|
||||
public function studyplan() : studyplan {
|
||||
return $this->studyplan;
|
||||
}
|
||||
|
||||
|
@ -47,7 +48,8 @@ class studyline {
|
|||
global $DB;
|
||||
$this->id = $id;
|
||||
$this->r = $DB->get_record(self::TABLE,['id' => $id]);
|
||||
$this->studyplan = studyplan::findById($this->r->studyplan_id);
|
||||
$this->page = studyplanpage::findById($this->r->page_id);
|
||||
$this->studyplan = $this->page->studyplan();
|
||||
}
|
||||
|
||||
public function id(){
|
||||
|
@ -105,7 +107,7 @@ class studyline {
|
|||
// make sure there are enought slots to account for them
|
||||
// Alternatively, we could ensure that on reduction of slots, the items that no longer have a slot will be removed.
|
||||
$max_slot = $DB->get_field_select(studyitem::TABLE,"MAX(slot)","line_id = :lineid",['lineid' => $this->id]);
|
||||
$num_slots = max($this->studyplan->slots(),$max_slot +1);
|
||||
$num_slots = max($this->page->periods(),$max_slot +1);
|
||||
|
||||
// Create the required amount of slots
|
||||
for($i=0; $i < $num_slots+1; $i++){
|
||||
|
@ -150,13 +152,13 @@ class studyline {
|
|||
public static function add($fields){
|
||||
global $DB;
|
||||
|
||||
if(!isset($fields['studyplan_id'])){
|
||||
throw new \InvalidArgumentException("parameter 'studyplan_id' missing");
|
||||
if(!isset($fields['page_id'])){
|
||||
throw new \InvalidArgumentException("parameter 'page_id' missing");
|
||||
}
|
||||
|
||||
$studyplan_id = $fields['studyplan_id'];
|
||||
$sqmax = $DB->get_field_select(self::TABLE,"MAX(sequence)","studyplan_id = :studyplan_id",['studyplan_id' => $studyplan_id]);
|
||||
$addable = ['studyplan_id','name','shortname','color'];
|
||||
$page_id = $fields['page_id'];
|
||||
$sqmax = $DB->get_field_select(self::TABLE,"MAX(sequence)","page_id = :page_id",['page_id' => $page_id]);
|
||||
$addable = ['page_id','name','shortname','color'];
|
||||
$info = ['sequence' => $sqmax+1];
|
||||
foreach($addable as $f){
|
||||
if(array_key_exists($f,$fields)){
|
||||
|
@ -217,11 +219,12 @@ class studyline {
|
|||
return success::success();
|
||||
}
|
||||
|
||||
public static function find_studyplan_children(studyplan $plan)
|
||||
public static function find_page_children(studyplanpage $page)
|
||||
{
|
||||
global $DB;
|
||||
$list = [];
|
||||
$ids = $DB->get_fieldset_select(self::TABLE,"id","studyplan_id = :plan_id ORDER BY sequence",['plan_id' => $plan->id()]);
|
||||
$ids = $DB->get_fieldset_select(self::TABLE,"id","page_id = :page_id ORDER BY sequence",
|
||||
['page_id' => $page->id()]);
|
||||
foreach($ids as $id) {
|
||||
$list[] = self::findById($id);
|
||||
}
|
||||
|
@ -263,7 +266,7 @@ class studyline {
|
|||
// make sure there are enought slots to account for them
|
||||
// Alternatively, we could ensure that on reduction of slots, the items that no longer have a slot will be removed.
|
||||
$max_slot = $DB->get_field_select(studyitem::TABLE,"MAX(slot)","line_id = :lineid",['lineid' => $this->id]);
|
||||
$num_slots = max($this->studyplan->slots(),$max_slot +1);
|
||||
$num_slots = max($this->page->periods(),$max_slot +1);
|
||||
|
||||
// Create the required amount of slots
|
||||
for($i=0; $i < $num_slots+1; $i++){
|
||||
|
@ -339,7 +342,7 @@ class studyline {
|
|||
$filterlayer = 0;
|
||||
foreach($slotmodel as $itemmodel)
|
||||
{
|
||||
if($itemmodel["type"] == "course" || $itemmodel["type"] == "competency"){
|
||||
if($itemmodel["type"] == "course"){
|
||||
$itemmodel["layer"] = $courselayer;
|
||||
$courselayer++;
|
||||
}else {
|
||||
|
|
|
@ -15,8 +15,9 @@ class studyplan {
|
|||
private $aggregator;
|
||||
private $context = null; // Hold context object once retrieved
|
||||
private $linked_userids = null; // cache lookup of linked users (saves queries)
|
||||
private $page_cache = null;
|
||||
|
||||
public function getAggregator(){
|
||||
public function aggregator(){
|
||||
return $this->aggregator;
|
||||
}
|
||||
|
||||
|
@ -44,10 +45,6 @@ class studyplan {
|
|||
return $this->r->shortname;
|
||||
}
|
||||
|
||||
public function slots(){
|
||||
return $this->r->slots;
|
||||
}
|
||||
|
||||
public function name(){
|
||||
return $this->r->name;
|
||||
}
|
||||
|
@ -56,6 +53,16 @@ class studyplan {
|
|||
return new \DateTime($this->r->startdate);
|
||||
}
|
||||
|
||||
public function pages(){
|
||||
// cached version of find_studyplan_children.
|
||||
// (may be premature optimization, since
|
||||
// find_studyplan_children also does some caching)
|
||||
if(empty($this->page_cache)){
|
||||
$this->page_cache = studyplanpage::find_studyplan_children($this);
|
||||
}
|
||||
return $this->page_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the context this studyplan is associated to
|
||||
*/
|
||||
|
@ -71,27 +78,13 @@ class studyplan {
|
|||
return $this->context;
|
||||
}
|
||||
|
||||
public function enddate(){
|
||||
if($this->r->enddate && strlen($this->r->enddate) > 0){
|
||||
return new \DateTime($this->r->enddate);
|
||||
}
|
||||
else{
|
||||
// return a date 100 years into the future
|
||||
return (new \DateTime($this->r->startdate))->add(new \DateInterval("P100Y"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function simple_structure($value=VALUE_REQUIRED){
|
||||
return new \external_single_structure([
|
||||
"id" => new \external_value(PARAM_INT, 'id of studyplan'),
|
||||
"name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
|
||||
"shortname"=> new \external_value(PARAM_TEXT, 'shortname of studyplan'),
|
||||
"slots" => new \external_value(PARAM_INT, 'number of slots in studyplan'),
|
||||
"context_id" => new \external_value(PARAM_INT, 'context_id of studyplan'),
|
||||
"description"=> new \external_value(PARAM_TEXT, 'description of studyplan'),
|
||||
"startdate" => new \external_value(PARAM_TEXT, 'start date of studyplan'),
|
||||
"enddate" => new \external_value(PARAM_TEXT, 'end date of studyplan'),
|
||||
"aggregation" => new \external_value(PARAM_TEXT, 'selected aggregator'),
|
||||
"aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator'),
|
||||
"aggregation_info" => aggregator::basic_structure(),
|
||||
|
@ -103,11 +96,8 @@ class studyplan {
|
|||
'id' => $this->r->id,
|
||||
'name' => $this->r->name,
|
||||
'shortname' => $this->r->shortname,
|
||||
'slots' => $this->r->slots,
|
||||
'context_id' => $this->context()->id,
|
||||
'description' => $this->r->description,
|
||||
'startdate' => $this->r->startdate,
|
||||
'enddate' => $this->r->enddate,
|
||||
"aggregation" => $this->r->aggregation,
|
||||
"aggregation_config" => $this->aggregator->config_string(),
|
||||
'aggregation_info' => $this->aggregator->basic_model(),
|
||||
|
@ -120,18 +110,11 @@ class studyplan {
|
|||
"name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
|
||||
"shortname"=> new \external_value(PARAM_TEXT, 'shortname of studyplan'),
|
||||
"description"=> new \external_value(PARAM_TEXT, 'description of studyplan'),
|
||||
"slots" => new \external_value(PARAM_INT, 'number of slots in studyplan'),
|
||||
"context_id" => new \external_value(PARAM_INT, 'context_id of studyplan'),
|
||||
"startdate" => new \external_value(PARAM_TEXT, 'start date of studyplan'),
|
||||
"enddate" => new \external_value(PARAM_TEXT, 'end date of studyplan'),
|
||||
"aggregation" => new \external_value(PARAM_TEXT, 'selected aggregator'),
|
||||
"aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator'),
|
||||
"aggregation_info" => aggregator::basic_structure(),
|
||||
/*"association" => new \external_single_structure([
|
||||
'cohorts' => new \external_multiple_structure(associationservice::cohort_structure()),
|
||||
'users' => new \external_multiple_structure(associationservice::user_structure()),
|
||||
]),*/
|
||||
"studylines" => new \external_multiple_structure(studyline::editor_structure()),
|
||||
"pages" => new \external_multiple_structure(studyplanpage::editor_structure()),
|
||||
"advanced" => new \external_single_structure([
|
||||
"force_scales" => new \external_single_structure([
|
||||
"scales" => new \external_multiple_structure(new \external_single_structure([
|
||||
|
@ -151,24 +134,16 @@ class studyplan {
|
|||
'name' => $this->r->name,
|
||||
'shortname' => $this->r->shortname,
|
||||
'description' => $this->r->description,
|
||||
'slots' => $this->r->slots,
|
||||
'context_id' => $this->context()->id,
|
||||
'startdate' => $this->r->startdate,
|
||||
'enddate' => $this->r->enddate,
|
||||
"aggregation" => $this->r->aggregation,
|
||||
"aggregation_config" => $this->aggregator->config_string(),
|
||||
'aggregation_info' => $this->aggregator->basic_model(),
|
||||
/*'association' => [
|
||||
'cohorts' => associationservice::associated_cohorts($this->r->id),
|
||||
'users' => associationservice::associated_users($this->r->id),
|
||||
],*/
|
||||
'studylines' => [],
|
||||
'aggregation_info' => $this->aggregator->basic_model(),
|
||||
'pages' => [],
|
||||
];
|
||||
|
||||
$children = studyline::find_studyplan_children($this);
|
||||
foreach($children as $c)
|
||||
foreach($this->pages() as $p)
|
||||
{
|
||||
$model['studylines'][] = $c->editor_model();
|
||||
$model['pages'][] = $p->editor_model();
|
||||
}
|
||||
|
||||
if(has_capability('local/treestudyplan:forcescales', \context_system::instance())){
|
||||
|
@ -231,14 +206,14 @@ class studyplan {
|
|||
global $DB;
|
||||
|
||||
if($force){
|
||||
$children = studyline::find_studyplan_children($this);
|
||||
$children = studyplanpage::find_studyplan_children($this);
|
||||
foreach($children as $c){
|
||||
$c->delete($force);
|
||||
}
|
||||
}
|
||||
|
||||
if($DB->count_records('local_treestudyplan_line',['studyplan_id' => $this->id]) > 0){
|
||||
return success::fail('cannot delete studyplan that still has studylines');
|
||||
if($DB->count_records('local_treestudyplan_page',['studyplan_id' => $this->id]) > 0){
|
||||
return success::fail('cannot delete studyplan that still has pages');
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -407,10 +382,7 @@ class studyplan {
|
|||
"name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
|
||||
"shortname"=> new \external_value(PARAM_TEXT, 'shortname of studyplan'),
|
||||
"description"=> new \external_value(PARAM_TEXT, 'description of studyplan'),
|
||||
"slots" => new \external_value(PARAM_INT, 'number of slots in studyplan'),
|
||||
"startdate" => new \external_value(PARAM_TEXT, 'start date of studyplan'),
|
||||
"enddate" => new \external_value(PARAM_TEXT, 'end date of studyplan'),
|
||||
"studylines" => new \external_multiple_structure(studyline::user_structure()),
|
||||
"pages" => new \external_multiple_structure(studyplanpage::user_structure()),
|
||||
"aggregation_info" => aggregator::basic_structure(),
|
||||
],'Studyplan with user info',$value);
|
||||
|
||||
|
@ -423,17 +395,13 @@ class studyplan {
|
|||
'name' => $this->r->name,
|
||||
'shortname' => $this->r->shortname,
|
||||
'description' => $this->r->description,
|
||||
'slots' => $this->r->slots,
|
||||
'startdate' => $this->r->startdate,
|
||||
'enddate' => $this->r->enddate,
|
||||
'studylines' => [],
|
||||
'pages' => [],
|
||||
'aggregation_info' => $this->aggregator->basic_model(),
|
||||
];
|
||||
|
||||
$children = studyline::find_studyplan_children($this);
|
||||
foreach($children as $c)
|
||||
foreach($this->pages() as $p)
|
||||
{
|
||||
$model['studylines'][] = $c->user_model($userid);
|
||||
$model['pages'][] = $p->user_model($userid);
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
@ -459,24 +427,10 @@ class studyplan {
|
|||
|
||||
// next, copy the studylines
|
||||
|
||||
$children = studyline::find_studyplan_children($this);
|
||||
$itemtranslation = [];
|
||||
$linetranslation = [];
|
||||
foreach($children as $c){
|
||||
$newchild = $c->duplicate($this,$itemtranslation);
|
||||
$linetranslation[$c->id()] = $newchild->id();
|
||||
foreach($this->pages() as $p){
|
||||
$newchild = $p->duplicate($this);
|
||||
}
|
||||
|
||||
// now the itemtranslation array contains all of the old child id's as keys and all of the related new ids as values
|
||||
// (feature of the studyline::duplicate function)
|
||||
// use this to recreate the lines in the new plan
|
||||
foreach(array_keys($itemtranslation) as $item_id){
|
||||
// copy based on the outgoing connections of each item, to avoid duplicates
|
||||
$connections = studyitemconnection::find_outgoing($item_id);
|
||||
foreach($connections as $conn){
|
||||
studyitemconnection::connect($itemtranslation[$conn->from_id],$itemtranslation[$conn->to_id]);
|
||||
}
|
||||
}
|
||||
return $new;
|
||||
}
|
||||
|
||||
|
@ -493,95 +447,12 @@ class studyplan {
|
|||
$model = $this->export_model();
|
||||
$json = json_encode([
|
||||
"type"=>"studyplan",
|
||||
"version"=>1.0,
|
||||
"version"=>2.0,
|
||||
"studyplan"=>$model
|
||||
],\JSON_PRETTY_PRINT);
|
||||
return [ "format" => "application/json", "content" => $json];
|
||||
}
|
||||
|
||||
public function export_plan_csv()
|
||||
{
|
||||
$model = $this->editor_model();
|
||||
|
||||
$slots = intval($model["slots"]);
|
||||
// First line
|
||||
$csv = "\"Studyline[{$slots}]\"";
|
||||
for($i = 1; $i <= $slots; $i++){
|
||||
$csv .= ",\"P{$i}\"";
|
||||
}
|
||||
$csv .= "\r\n";
|
||||
// next, make one line per studyline
|
||||
foreach($model["studylines"] as $line){
|
||||
// determine how many fields are simultaneous in the line at maximum
|
||||
$maxlines = 1;
|
||||
for($i = 1; $i <= $slots; $i++){
|
||||
if(count($line["slots"]) > $i){
|
||||
$ct = 0;
|
||||
foreach($line["slots"][$i][studyline::SLOTSET_COMPETENCY] as $itm){
|
||||
if($itm["type"] == "course"){
|
||||
$ct += 1;
|
||||
}
|
||||
}
|
||||
if($ct > $maxlines){
|
||||
$maxlines = $ct;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for($lct = 0; $lct < $maxlines; $lct++){
|
||||
$csv .= "\"{$line["name"]}\"";
|
||||
for($i = 1; $i <= $slots; $i++){
|
||||
$filled = false;
|
||||
if(count($line["slots"]) > $i){
|
||||
$ct = 0;
|
||||
foreach($line["slots"][$i][studyline::SLOTSET_COMPETENCY] as $itm){
|
||||
if($itm["type"] == "course"){
|
||||
if($ct == $lct){
|
||||
$csv .= ",\"";
|
||||
$csv .= $itm["course"]["fullname"];
|
||||
$csv .= "\r\n";
|
||||
$first = true;
|
||||
foreach($itm["course"]["grades"] as $g){
|
||||
if($g["selected"]){
|
||||
if($first){
|
||||
$first = false;
|
||||
}
|
||||
else{
|
||||
$csv .= "\r\n";
|
||||
}
|
||||
$csv .= "- ".str_replace('"', '\'', $g["name"]);
|
||||
}
|
||||
}
|
||||
$csv .= "\"";
|
||||
$filled = true;
|
||||
break;
|
||||
}
|
||||
$ct++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!$filled) {
|
||||
$csv .= ",\"\"";
|
||||
}
|
||||
}
|
||||
$csv .= "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
return [ "format" => "text/csv", "content" => $csv];
|
||||
}
|
||||
|
||||
public function export_studylines(){
|
||||
$model = $this->export_studylines_model();
|
||||
$json = json_encode([
|
||||
"type"=>"studylines",
|
||||
"version"=>1.0,
|
||||
"studylines"=>$model,
|
||||
],\JSON_PRETTY_PRINT);
|
||||
return [ "format" => "application/json", "content" => $json];
|
||||
}
|
||||
|
||||
public function export_model()
|
||||
{
|
||||
$model = [
|
||||
|
@ -594,20 +465,19 @@ class studyplan {
|
|||
"aggregation" => $this->r->aggregation,
|
||||
"aggregation_config" => json_decode($this->aggregator->config_string()),
|
||||
'aggregation_info' => $this->aggregator->basic_model(),
|
||||
'studylines' => $this->export_studylines_model(),
|
||||
'pages' => $this->export_pages_model(),
|
||||
];
|
||||
return $model;
|
||||
}
|
||||
|
||||
public function export_studylines_model()
|
||||
public function export_pages_model()
|
||||
{
|
||||
$children = studyline::find_studyplan_children($this);
|
||||
$lines = [];
|
||||
foreach($children as $c)
|
||||
$pages = [];
|
||||
foreach($this->pages() as $p)
|
||||
{
|
||||
$lines[] = $c->export_model();
|
||||
$pages[] = $p->export_model();
|
||||
}
|
||||
return $lines;
|
||||
return $pages;
|
||||
}
|
||||
|
||||
public static function import_studyplan($content,$format="application/json",$context_id=1)
|
||||
|
@ -615,15 +485,18 @@ class studyplan {
|
|||
if($format != "application/json") { return false;}
|
||||
|
||||
$content = json_decode($content,true);
|
||||
if($content["type"] == "studyplan" && $content["version"] >= 1.0){
|
||||
if($content["type"] == "studyplan" && $content["version"] >= 2.0){
|
||||
|
||||
// Make sure the aggregation_config is re-encoded as json text
|
||||
$content["studyplan"]["aggregation_config"] = json_encode($content["studyplan"]["aggregation_config"]);
|
||||
|
||||
// And make sure the context_id is set to the
|
||||
// And make sure the context_id is set to the provided context for import
|
||||
$content["studyplan"]["context_id"] = $context_id;
|
||||
|
||||
// Create a new plan, based on the given parameters - this is the import studyplan part
|
||||
$plan = self::add($content["studyplan"]);
|
||||
return $plan->import_studylines_model($content["studyplan"]["studylines"]);
|
||||
// Now import each page
|
||||
return $plan->import_pages_model($content["studyplan"]["pages"]);
|
||||
|
||||
}
|
||||
else {
|
||||
|
@ -632,58 +505,33 @@ class studyplan {
|
|||
}
|
||||
}
|
||||
|
||||
public function import_studylines($content,$format="application/json")
|
||||
public function import_pages($content,$format="application/json")
|
||||
{
|
||||
if($format != "application/json") { return false;}
|
||||
$content = json_decode($content,true);
|
||||
if($content["type"] == "studylines" && $content["version"] >= 1.0){
|
||||
return $this->import_studylines_model($content["studylines"]);
|
||||
}
|
||||
else if($content["type"] == "studyplan" && $content["version"] >= 1.0){
|
||||
return $this->import_studylines_model($content["studyplan"]["studylines"]);
|
||||
if($content["version"] >= 2.0){
|
||||
if($content["type"] == "studyplanpage"){
|
||||
// import single page from a studyplanpage (wrapped in array of one page)
|
||||
return $this->import_pages_model([$content["page"]]);
|
||||
}
|
||||
else if($content["type"] == "studyplan"){
|
||||
// Import all pages from the studyplan
|
||||
return $this->import_pages_model($content["studyplan"]["pages"]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function find_studyline_by_shortname($shortname){
|
||||
$children = studyline::find_studyplan_children($this);
|
||||
foreach($children as $l){
|
||||
if($shortname == $l->shortname()){
|
||||
return $l;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function import_studylines_model($model)
|
||||
protected function import_pages_model($model)
|
||||
{
|
||||
// First attempt to map each studyline model to an existing or new line
|
||||
$line_map = [];
|
||||
foreach($model as $ix => $linemodel){
|
||||
$line = $this->find_studyline_by_shortname($linemodel["shortname"]);
|
||||
if(empty($line)){
|
||||
$linemodel["studyplan_id"] = $this->id;
|
||||
$line = studyline::add($linemodel);
|
||||
} else {
|
||||
//$line->edit($linemodel); // Update the line with the settings from the imported file
|
||||
}
|
||||
$line_map[$ix] = $line;
|
||||
}
|
||||
|
||||
// next, let each study line import the study items
|
||||
$itemtranslation = [];
|
||||
$connections = [];
|
||||
foreach($model as $ix => $linemodel){
|
||||
$line_map[$ix]->import_studyitems($linemodel["slots"],$itemtranslation,$connections);
|
||||
}
|
||||
|
||||
// Finally, create the links between the study items
|
||||
foreach($connections as $from => $dests){
|
||||
foreach($dests as $to){
|
||||
studyitemconnection::connect($from,$itemtranslation[$to]);
|
||||
}
|
||||
$this->pages(); // make sure the page cache is initialized, since we will be adding to it.
|
||||
foreach($model as $p){
|
||||
$p["studyplan_id"] = $this->id();
|
||||
$page = studyplanpage::add($p);
|
||||
$this->page_cache[] = $page;
|
||||
$page->import_studylines_model($p["studylines"]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
544
classes/studyplanpage.php
Normal file
544
classes/studyplanpage.php
Normal file
|
@ -0,0 +1,544 @@
|
|||
<?php
|
||||
namespace local_treestudyplan;
|
||||
|
||||
require_once($CFG->libdir.'/externallib.php');
|
||||
|
||||
class studyplanpage {
|
||||
|
||||
|
||||
const TABLE = "local_treestudyplan_page";
|
||||
private static $CACHE = [];
|
||||
|
||||
|
||||
private $r; // Holds database record
|
||||
private $id;
|
||||
private $studyplan;
|
||||
|
||||
public function aggregator(){
|
||||
return $this->studyplan->aggregator();
|
||||
}
|
||||
|
||||
// Cache constructors to avoid multiple creation events in one session.
|
||||
public static function findById($id): self {
|
||||
if(!array_key_exists($id,self::$CACHE)){
|
||||
self::$CACHE[$id] = new self($id);
|
||||
}
|
||||
return self::$CACHE[$id];
|
||||
}
|
||||
|
||||
private function __construct($id) {
|
||||
global $DB;
|
||||
$this->id = $id;
|
||||
$this->r = $DB->get_record(self::TABLE,['id' => $id]);
|
||||
$this->studyplan = studyplan::findById($this->r->studyplan_id);
|
||||
}
|
||||
|
||||
public function id(){
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function studyplan() : studyplan {
|
||||
return $this->studyplan;
|
||||
}
|
||||
|
||||
public function shortname(){
|
||||
return $this->r->shortname;
|
||||
}
|
||||
|
||||
public function periods(){
|
||||
return $this->r->periods;
|
||||
}
|
||||
|
||||
public function fullname(){
|
||||
return $this->r->fullname;
|
||||
}
|
||||
|
||||
public function startdate(){
|
||||
return new \DateTime($this->r->startdate);
|
||||
}
|
||||
|
||||
public function enddate(){
|
||||
if($this->r->enddate && strlen($this->r->enddate) > 0){
|
||||
return new \DateTime($this->r->enddate);
|
||||
}
|
||||
else{
|
||||
// return a date 100 years into the future
|
||||
return (new \DateTime($this->r->startdate))->add(new \DateInterval("P100Y"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function simple_structure($value=VALUE_REQUIRED){
|
||||
return new \external_single_structure([
|
||||
"id" => new \external_value(PARAM_INT, 'id of studyplan page'),
|
||||
"fullname" => new \external_value(PARAM_TEXT, 'name of studyplan page'),
|
||||
"shortname"=> new \external_value(PARAM_TEXT, 'shortname of studyplan page'),
|
||||
"periods" => new \external_value(PARAM_INT, 'number of periods in studyplan page'),
|
||||
"description"=> new \external_value(PARAM_TEXT, 'description of studyplan page'),
|
||||
"startdate" => new \external_value(PARAM_TEXT, 'start date of studyplan'),
|
||||
"enddate" => new \external_value(PARAM_TEXT, 'end date of studyplan'),
|
||||
],'Studyplan page basic info',$value);
|
||||
}
|
||||
|
||||
public function simple_model(){
|
||||
return [
|
||||
'id' => $this->r->id,
|
||||
'fullname' => $this->r->fullname,
|
||||
'shortname' => $this->r->shortname,
|
||||
'periods' => $this->r->periods,
|
||||
'description' => $this->r->description,
|
||||
'startdate' => $this->r->startdate,
|
||||
'enddate' => $this->r->enddate,
|
||||
];
|
||||
}
|
||||
|
||||
public static function editor_structure($value=VALUE_REQUIRED){
|
||||
return new \external_single_structure([
|
||||
"id" => new \external_value(PARAM_INT, 'id of studyplan'),
|
||||
"name" => new \external_value(PARAM_TEXT, 'name of studyplan page'),
|
||||
"shortname"=> new \external_value(PARAM_TEXT, 'shortname of studyplan page'),
|
||||
"description"=> new \external_value(PARAM_TEXT, 'description of studyplan page'),
|
||||
"periods" => new \external_value(PARAM_INT, 'number of periods in studyplan page'),
|
||||
"startdate" => new \external_value(PARAM_TEXT, 'start date of studyplan page'),
|
||||
"enddate" => new \external_value(PARAM_TEXT, 'end date of studyplan page'),
|
||||
"studylines" => new \external_multiple_structure(studyline::editor_structure()),
|
||||
],'Studyplan page full structure',$value);
|
||||
}
|
||||
|
||||
public function editor_model(){
|
||||
global $DB;
|
||||
|
||||
$model = [
|
||||
'id' => $this->r->id,
|
||||
'name' => $this->r->name,
|
||||
'shortname' => $this->r->shortname,
|
||||
'description' => $this->r->description,
|
||||
'periods' => $this->r->periods,
|
||||
'startdate' => $this->r->startdate,
|
||||
'enddate' => $this->r->enddate,
|
||||
'studylines' => [],
|
||||
];
|
||||
|
||||
$children = studyline::find_page_children($this);
|
||||
foreach($children as $c)
|
||||
{
|
||||
$model['studylines'][] = $c->editor_model();
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
public static function add($fields){
|
||||
global $CFG, $DB;
|
||||
|
||||
if(!isset($fields['studyplan_id'])){
|
||||
throw new \InvalidArgumentException("parameter 'studyplan_id' missing");
|
||||
}
|
||||
|
||||
$addable = ['studyplan_id','fullname','shortname','description','periods','startdate','enddate'];
|
||||
$info = ['enddate' => null ];
|
||||
foreach($addable as $f){
|
||||
if(array_key_exists($f,$fields)){
|
||||
$info[$f] = $fields[$f];
|
||||
}
|
||||
}
|
||||
$id = $DB->insert_record(self::TABLE, $info);
|
||||
return self::findById($id); // make sure the new page is immediately cached
|
||||
}
|
||||
|
||||
public function edit($fields){
|
||||
global $DB;
|
||||
$editable = ['fullname','shortname','description','periods','startdate','enddate'];
|
||||
$info = ['id' => $this->id,];
|
||||
foreach($editable as $f){
|
||||
if(array_key_exists($f,$fields)){
|
||||
$info[$f] = $fields[$f];
|
||||
}
|
||||
}
|
||||
$DB->update_record(self::TABLE, $info);
|
||||
//reload record after edit
|
||||
$this->r = $DB->get_record(self::TABLE,['id' => $this->id],"*",MUST_EXIST);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function delete($force=false){
|
||||
global $DB;
|
||||
|
||||
if($force){
|
||||
$children = studyline::find_page_children($this);
|
||||
foreach($children as $c){
|
||||
$c->delete($force);
|
||||
}
|
||||
}
|
||||
|
||||
if($DB->count_records('local_treestudyplan_line',['studyplan_id' => $this->id]) > 0){
|
||||
return success::fail('cannot delete studyplan that still has studylines');
|
||||
}
|
||||
else
|
||||
{
|
||||
$DB->delete_records('local_treestudyplan_page', ['id' => $this->id]);
|
||||
return success::success();
|
||||
}
|
||||
}
|
||||
|
||||
public static function find_all($contextid=-1){
|
||||
global $DB, $USER;
|
||||
$list = [];
|
||||
|
||||
if($contextid <= 0){
|
||||
$ids = $DB->get_fieldset_select(self::TABLE,"id","");
|
||||
}
|
||||
else{
|
||||
if($contextid == 1){
|
||||
$contextid = 1;
|
||||
$where = "context_id <= :contextid OR context_id IS NULL";
|
||||
} else {
|
||||
$where = "context_id = :contextid";
|
||||
}
|
||||
$ids = $DB->get_fieldset_select(self::TABLE,"id",$where,["contextid" => $contextid]);
|
||||
}
|
||||
|
||||
foreach($ids as $id)
|
||||
{
|
||||
$list[] = self::findById($id);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public static function find_by_shortname($shortname, $contextid = 0): array{
|
||||
global $DB;
|
||||
$list = [];
|
||||
|
||||
$where = "shortname = :shortname AND context_id = :contextid";
|
||||
if($contextid == 0){
|
||||
$where .= "OR context_id IS NULL";
|
||||
}
|
||||
$ids = $DB->get_fieldset_select(self::TABLE,"id",$where,["shortname"=>$shortname, "contextid" => $contextid]);
|
||||
foreach($ids as $id)
|
||||
{
|
||||
$list[] = self::findById($id);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public static function find_for_user($userid)
|
||||
{
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT s.id FROM {local_treestudyplan} s
|
||||
INNER JOIN {local_treestudyplan_cohort} j ON j.studyplan_id = s.id
|
||||
INNER JOIN {cohort_members} cm ON j.cohort_id = cm.cohortid
|
||||
WHERE cm.userid = :userid";
|
||||
$cohort_plan_ids = $DB->get_fieldset_sql($sql, ['userid' => $userid]);
|
||||
|
||||
$sql = "SELECT s.id FROM {local_treestudyplan} s
|
||||
INNER JOIN {local_treestudyplan_user} j ON j.studyplan_id = s.id
|
||||
WHERE j.user_id = :userid";
|
||||
$user_plan_ids = $DB->get_fieldset_sql($sql, ['userid' => $userid]);
|
||||
|
||||
$plans = [];
|
||||
foreach($cohort_plan_ids as $id) {
|
||||
$plans[$id] = self::findById($id);
|
||||
}
|
||||
foreach($user_plan_ids as $id) {
|
||||
if(!array_key_exists($id,$plans)){
|
||||
$plans[$id] = self::findById($id);
|
||||
}
|
||||
}
|
||||
|
||||
return $plans;
|
||||
|
||||
}
|
||||
|
||||
static public function exist_for_user($userid)
|
||||
{
|
||||
global $DB;
|
||||
$count = 0;
|
||||
$sql = "SELECT s.* FROM {local_treestudyplan} s
|
||||
INNER JOIN {local_treestudyplan_cohort} j ON j.studyplan_id = s.id
|
||||
INNER JOIN {cohort_members} cm ON j.cohort_id = cm.cohortid
|
||||
WHERE cm.userid = :userid";
|
||||
$count += $DB->count_records_sql($sql, ['userid' => $userid]);
|
||||
|
||||
$sql = "SELECT s.* FROM {local_treestudyplan} s
|
||||
INNER JOIN {local_treestudyplan_user} j ON j.studyplan_id = s.id
|
||||
WHERE j.user_id = :userid";
|
||||
$count += $DB->count_records_sql($sql, ['userid' => $userid]);
|
||||
|
||||
return ($count > 0);
|
||||
}
|
||||
|
||||
public static function user_structure($value=VALUE_REQUIRED){
|
||||
return new \external_single_structure([
|
||||
"id" => new \external_value(PARAM_INT, 'id of studyplan page'),
|
||||
"fullname" => new \external_value(PARAM_TEXT, 'name of studyplan page'),
|
||||
"shortname"=> new \external_value(PARAM_TEXT, 'shortname of studyplan page'),
|
||||
"description"=> new \external_value(PARAM_TEXT, 'description of studyplan page'),
|
||||
"periods" => new \external_value(PARAM_INT, 'number of slots in studyplan page'),
|
||||
"startdate" => new \external_value(PARAM_TEXT, 'start date of studyplan page'),
|
||||
"enddate" => new \external_value(PARAM_TEXT, 'end date of studyplan page'),
|
||||
"studylines" => new \external_multiple_structure(studyline::user_structure()),
|
||||
],'Studyplan page with user info',$value);
|
||||
}
|
||||
|
||||
public function user_model($userid){
|
||||
|
||||
$model = [
|
||||
'id' => $this->r->id,
|
||||
'fullname' => $this->r->name,
|
||||
'shortname' => $this->r->shortname,
|
||||
'description' => $this->r->description,
|
||||
'periods' => $this->r->periods,
|
||||
'startdate' => $this->r->startdate,
|
||||
'enddate' => $this->r->enddate,
|
||||
'studylines' => [],
|
||||
];
|
||||
|
||||
$children = studyline::find_page_children($this);
|
||||
foreach($children as $c)
|
||||
{
|
||||
$model['studylines'][] = $c->user_model($userid);
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
public static function find_studyplan_children(studyplan $plan)
|
||||
{
|
||||
global $DB;
|
||||
$list = [];
|
||||
$ids = $DB->get_fieldset_select(self::TABLE,"id","studyplan_id = :plan_id ORDER BY startdate",
|
||||
['plan_id' => $plan->id()]);
|
||||
foreach($ids as $id) {
|
||||
$list[] = self::findById($id);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public static function duplicate_page($page_id,$name,$shortname)
|
||||
{
|
||||
$ori = self::findById($page_id);
|
||||
$new = $ori->duplicate($name,$shortname);
|
||||
return $new->simple_model();
|
||||
}
|
||||
|
||||
public function duplicate($name,$shortname)
|
||||
{
|
||||
// First duplicate the studyplan structure
|
||||
$new = studyplanpage::add([
|
||||
'fullname' => $name,
|
||||
'shortname' => $shortname,
|
||||
'description' => $this->r->description,
|
||||
'pages' => $this->r->pages,
|
||||
'startdate' => $this->r->startdate,
|
||||
'enddate' => empty($this->r->enddate)?null:$this->r->enddate,
|
||||
]);
|
||||
|
||||
// next, copy the studylines
|
||||
|
||||
$children = studyline::find_page_children($this);
|
||||
$itemtranslation = [];
|
||||
$linetranslation = [];
|
||||
foreach($children as $c){
|
||||
$newchild = $c->duplicate($this,$itemtranslation);
|
||||
$linetranslation[$c->id()] = $newchild->id();
|
||||
}
|
||||
|
||||
// now the itemtranslation array contains all of the old child id's as keys and all of the related new ids as values
|
||||
// (feature of the studyline::duplicate function)
|
||||
// use this to recreate the lines in the new plan
|
||||
foreach(array_keys($itemtranslation) as $item_id){
|
||||
// copy based on the outgoing connections of each item, to avoid duplicates
|
||||
$connections = studyitemconnection::find_outgoing($item_id);
|
||||
foreach($connections as $conn){
|
||||
studyitemconnection::connect($itemtranslation[$conn->from_id],$itemtranslation[$conn->to_id]);
|
||||
}
|
||||
}
|
||||
return $new;
|
||||
}
|
||||
|
||||
public static function export_structure()
|
||||
{
|
||||
return new \external_single_structure([
|
||||
"format" => new \external_value(PARAM_TEXT, 'format of studyplan export'),
|
||||
"content"=> new \external_value(PARAM_TEXT, 'exported studyplan content'),
|
||||
],'Exported studyplan');
|
||||
}
|
||||
|
||||
public function export_page()
|
||||
{
|
||||
$model = $this->export_model();
|
||||
$json = json_encode([
|
||||
"type"=>"studyplanpage",
|
||||
"version"=>2.0,
|
||||
"page"=>$model
|
||||
],\JSON_PRETTY_PRINT);
|
||||
return [ "format" => "application/json", "content" => $json];
|
||||
}
|
||||
|
||||
public function export_page_csv()
|
||||
{
|
||||
//TODO: Period shortnames instead of just P1, P2, P3 etc
|
||||
|
||||
$model = $this->editor_model();
|
||||
|
||||
$slots = intval($model["slots"]);
|
||||
// First line
|
||||
$csv = "\"Studyline[{$slots}]\"";
|
||||
for($i = 1; $i <= $slots; $i++){
|
||||
$csv .= ",\"P{$i}\"";
|
||||
}
|
||||
$csv .= "\r\n";
|
||||
// next, make one line per studyline
|
||||
foreach($model["studylines"] as $line){
|
||||
// determine how many fields are simultaneous in the line at maximum
|
||||
$maxlines = 1;
|
||||
for($i = 1; $i <= $slots; $i++){
|
||||
if(count($line["slots"]) > $i){
|
||||
$ct = 0;
|
||||
foreach($line["slots"][$i][studyline::SLOTSET_COMPETENCY] as $itm){
|
||||
if($itm["type"] == "course"){
|
||||
$ct += 1;
|
||||
}
|
||||
}
|
||||
if($ct > $maxlines){
|
||||
$maxlines = $ct;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for($lct = 0; $lct < $maxlines; $lct++){
|
||||
$csv .= "\"{$line["name"]}\"";
|
||||
for($i = 1; $i <= $slots; $i++){
|
||||
$filled = false;
|
||||
if(count($line["slots"]) > $i){
|
||||
$ct = 0;
|
||||
foreach($line["slots"][$i][studyline::SLOTSET_COMPETENCY] as $itm){
|
||||
if($itm["type"] == "course"){
|
||||
if($ct == $lct){
|
||||
$csv .= ",\"";
|
||||
$csv .= $itm["course"]["fullname"];
|
||||
$csv .= "\r\n";
|
||||
$first = true;
|
||||
foreach($itm["course"]["grades"] as $g){
|
||||
if($g["selected"]){
|
||||
if($first){
|
||||
$first = false;
|
||||
}
|
||||
else{
|
||||
$csv .= "\r\n";
|
||||
}
|
||||
$csv .= "- ".str_replace('"', '\'', $g["name"]);
|
||||
}
|
||||
}
|
||||
$csv .= "\"";
|
||||
$filled = true;
|
||||
break;
|
||||
}
|
||||
$ct++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!$filled) {
|
||||
$csv .= ",\"\"";
|
||||
}
|
||||
}
|
||||
$csv .= "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
return [ "format" => "text/csv", "content" => $csv];
|
||||
}
|
||||
|
||||
public function export_studylines(){
|
||||
$model = $this->export_studylines_model();
|
||||
$json = json_encode([
|
||||
"type"=>"studylines",
|
||||
"version"=>1.0,
|
||||
"studylines"=>$model,
|
||||
],\JSON_PRETTY_PRINT);
|
||||
return [ "format" => "application/json", "content" => $json];
|
||||
}
|
||||
|
||||
public function export_model()
|
||||
{
|
||||
$model = [
|
||||
'fullname' => $this->r->name,
|
||||
'shortname' => $this->r->shortname,
|
||||
'description' => $this->r->description,
|
||||
'periods' => $this->r->periods,
|
||||
'startdate' => $this->r->startdate,
|
||||
'enddate' => $this->r->enddate,
|
||||
'studylines' => $this->export_studylines_model(),
|
||||
];
|
||||
return $model;
|
||||
}
|
||||
|
||||
public function export_studylines_model()
|
||||
{
|
||||
$children = studyline::find_page_children($this);
|
||||
$lines = [];
|
||||
foreach($children as $c)
|
||||
{
|
||||
$lines[] = $c->export_model();
|
||||
}
|
||||
return $lines;
|
||||
}
|
||||
|
||||
public function import_studylines($content,$format="application/json")
|
||||
{
|
||||
if($format != "application/json") { return false;}
|
||||
$content = json_decode($content,true);
|
||||
if($content["type"] == "studylines" && $content["version"] >= 2.0){
|
||||
return $this->import_studylines_model($content["studylines"]);
|
||||
}
|
||||
else if($content["type"] == "studyplanpage" && $content["version"] >= 2.0){
|
||||
return $this->import_studylines_model($content["page"]["studylines"]);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function find_studyline_by_shortname($shortname){
|
||||
$children = studyline::find_page_children($this);
|
||||
foreach($children as $l){
|
||||
if($shortname == $l->shortname()){
|
||||
return $l;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function import_studylines_model($model)
|
||||
{
|
||||
// First attempt to map each studyline model to an existing or new line
|
||||
$line_map = [];
|
||||
foreach($model as $ix => $linemodel){
|
||||
$line = $this->find_studyline_by_shortname($linemodel["shortname"]);
|
||||
if(empty($line)){
|
||||
$linemodel["studyplan_id"] = $this->id;
|
||||
$line = studyline::add($linemodel);
|
||||
} else {
|
||||
//$line->edit($linemodel); // Update the line with the settings from the imported file
|
||||
}
|
||||
$line_map[$ix] = $line;
|
||||
}
|
||||
|
||||
// next, let each study line import the study items
|
||||
$itemtranslation = [];
|
||||
$connections = [];
|
||||
foreach($model as $ix => $linemodel){
|
||||
$line_map[$ix]->import_studyitems($linemodel["slots"],$itemtranslation,$connections);
|
||||
}
|
||||
|
||||
// Finally, create the links between the study items
|
||||
foreach($connections as $from => $dests){
|
||||
foreach($dests as $to){
|
||||
studyitemconnection::connect($from,$itemtranslation[$to]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -65,12 +65,14 @@ foreach($plans as $plan){
|
|||
cli_writeln(" - {$u->firstname} {$u->lastname} / {$u->username}");
|
||||
}
|
||||
|
||||
$lines = studyline::find_studyplan_children($plan);
|
||||
foreach($lines as $line){
|
||||
cli_writeln(" ** {$line->name()} **");
|
||||
$items = studyitem::find_studyline_children($line);
|
||||
foreach($users as $u){
|
||||
$generator->addskill($u->username,$line->shortname());
|
||||
foreach($plan->pages() as $page){
|
||||
$lines = studyline::find_page_children($page);
|
||||
foreach($lines as $line){
|
||||
cli_writeln(" ** {$line->name()} **");
|
||||
$items = studyitem::find_studyline_children($line);
|
||||
foreach($users as $u){
|
||||
$generator->addskill($u->username,$line->shortname());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,74 +89,76 @@ foreach($plans as $plan){
|
|||
cli_heading($plan->name());
|
||||
$users = $plan->find_linked_users();
|
||||
|
||||
$lines = studyline::find_studyplan_children($plan);
|
||||
foreach($lines as $line){
|
||||
cli_writeln(" ** {$line->name()} **");
|
||||
$items = studyitem::find_studyline_children($line);
|
||||
foreach($items as $item){
|
||||
if($item->type() == studyitem::COURSE) { // only handle courses for now
|
||||
$courseinfo = $item->getcourseinfo();
|
||||
cli_writeln(" # {$courseinfo->shortname()}");
|
||||
foreach($plan->pages() as $page){
|
||||
$lines = studyline::find_page_children($page);
|
||||
foreach($lines as $line){
|
||||
cli_writeln(" ** {$line->name()} **");
|
||||
$items = studyitem::find_studyline_children($line);
|
||||
foreach($items as $item){
|
||||
if($item->type() == studyitem::COURSE) { // only handle courses for now
|
||||
$courseinfo = $item->getcourseinfo();
|
||||
cli_writeln(" # {$courseinfo->shortname()}");
|
||||
|
||||
if($courseinfo->course()->startdate <= time()){
|
||||
if($courseinfo->course()->startdate <= time()){
|
||||
|
||||
foreach($users as $u){
|
||||
cli_writeln(" -> {$u->firstname} {$u->lastname} <-");
|
||||
$gradables = gradeinfo::list_studyitem_gradables($item);
|
||||
$gen = $generator->generate($u->username,$line->shortname(),$gradables);
|
||||
foreach($gen as $gg){
|
||||
$g = $gg->gi;
|
||||
$gi = $g->getGradeitem();
|
||||
foreach($users as $u){
|
||||
cli_writeln(" -> {$u->firstname} {$u->lastname} <-");
|
||||
$gradables = gradeinfo::list_studyitem_gradables($item);
|
||||
$gen = $generator->generate($u->username,$line->shortname(),$gradables);
|
||||
foreach($gen as $gg){
|
||||
$g = $gg->gi;
|
||||
$gi = $g->getGradeitem();
|
||||
|
||||
$name = $gi->itemname;
|
||||
$grade = $gg->gradetext;
|
||||
cli_write (" - {$name} = {$grade}");
|
||||
|
||||
// Check if the item is alreaady graded for this user
|
||||
$existing = $count = $DB->count_records_select('grade_grades','itemid = :gradeitemid AND finalgrade IS NOT NULL and userid = :userid',
|
||||
['gradeitemid' => $gi->id, 'userid' => $u->id]);
|
||||
$name = $gi->itemname;
|
||||
$grade = $gg->gradetext;
|
||||
cli_write (" - {$name} = {$grade}");
|
||||
|
||||
// Check if the item is alreaady graded for this user
|
||||
$existing = $count = $DB->count_records_select('grade_grades','itemid = :gradeitemid AND finalgrade IS NOT NULL and userid = :userid',
|
||||
['gradeitemid' => $gi->id, 'userid' => $u->id]);
|
||||
|
||||
if(!$existing){
|
||||
if($gg->grade > 0){
|
||||
if($gi->itemmodule == "assign"){
|
||||
// If it is an assignment, submit though that interface
|
||||
list($c,$cminfo) = get_course_and_cm_from_instance($gi->iteminstance,$gi->itemmodule);
|
||||
$cm_ctx = \context_module::instance($cminfo->id);
|
||||
$a = new \assign($cm_ctx,$cminfo,$c);
|
||||
if(!$existing){
|
||||
if($gg->grade > 0){
|
||||
if($gi->itemmodule == "assign"){
|
||||
// If it is an assignment, submit though that interface
|
||||
list($c,$cminfo) = get_course_and_cm_from_instance($gi->iteminstance,$gi->itemmodule);
|
||||
$cm_ctx = \context_module::instance($cminfo->id);
|
||||
$a = new \assign($cm_ctx,$cminfo,$c);
|
||||
|
||||
$ug = $a->get_user_grade($u->id,true);
|
||||
$ug->grade = grade_floatval($gg->grade);
|
||||
$ug->grader = $USER->id;
|
||||
$ug->feedbacktext = nl2br( htmlspecialchars($gg->fb));
|
||||
$ug->feedbackformat = FORMAT_HTML;
|
||||
$ug = $a->get_user_grade($u->id,true);
|
||||
$ug->grade = grade_floatval($gg->grade);
|
||||
$ug->grader = $USER->id;
|
||||
$ug->feedbacktext = nl2br( htmlspecialchars($gg->fb));
|
||||
$ug->feedbackformat = FORMAT_HTML;
|
||||
|
||||
//print_r($ug);
|
||||
if(!$options["dryrun"]){
|
||||
$a->update_grade($ug);
|
||||
|
||||
grade_regrade_final_grades($c->id,$u->id,$gi);
|
||||
cli_writeln(" ... Stored");
|
||||
} else {
|
||||
cli_writeln(" ... (Dry Run)");
|
||||
}
|
||||
|
||||
//print_r($ug);
|
||||
if(!$options["dryrun"]){
|
||||
$a->update_grade($ug);
|
||||
|
||||
grade_regrade_final_grades($c->id,$u->id,$gi);
|
||||
cli_writeln(" ... Stored");
|
||||
} else {
|
||||
cli_writeln(" ... (Dry Run)");
|
||||
// Otherwise, set the grade through the manual grading override
|
||||
cli_writeln(" ... Cannot store");
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
// Otherwise, set the grade through the manual grading override
|
||||
cli_writeln(" ... Cannot store");
|
||||
|
||||
cli_writeln(" ... No grade");
|
||||
}
|
||||
} else {
|
||||
cli_writeln(" ... No grade");
|
||||
cli_writeln(" ... Already graded");
|
||||
}
|
||||
} else {
|
||||
cli_writeln(" ... Already graded");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cli_writeln(" Skipping since it has not started yet");
|
||||
else
|
||||
{
|
||||
cli_writeln(" Skipping since it has not started yet");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<XMLDB PATH="local/treestudyplan/db" VERSION="20230719" COMMENT="XMLDB file for Moodle local/treestudyplan"
|
||||
<XMLDB PATH="local/treestudyplan/db" VERSION="20230720" COMMENT="XMLDB file for Moodle local/treestudyplan"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
|
||||
>
|
||||
|
@ -27,9 +27,6 @@
|
|||
<FIELD NAME="name" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="shortname" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="description" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="slots" TYPE="int" LENGTH="9" NOTNULL="true" DEFAULT="4" SEQUENCE="false"/>
|
||||
<FIELD NAME="startdate" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="enddate" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="aggregation" TYPE="char" LENGTH="30" NOTNULL="true" DEFAULT="bistate" SEQUENCE="false"/>
|
||||
<FIELD NAME="aggregation_config" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="context_id" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
|
||||
|
|
|
@ -381,14 +381,41 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
|
|||
$recordset->close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Treestudyplan savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2023071900, 'local', 'treestudyplan');
|
||||
}
|
||||
if ($oldversion < 2023072000) {
|
||||
|
||||
// Define field slots to be dropped from local_treestudyplan.
|
||||
$table = new xmldb_table('local_treestudyplan');
|
||||
$field = new xmldb_field('slots');
|
||||
|
||||
// Conditionally launch drop field slots.
|
||||
if ($dbman->field_exists($table, $field)) {
|
||||
$dbman->drop_field($table, $field);
|
||||
}
|
||||
|
||||
// Define field startdate to be dropped from local_treestudyplan.
|
||||
$table = new xmldb_table('local_treestudyplan');
|
||||
$field = new xmldb_field('startdate');
|
||||
|
||||
// Conditionally launch drop field startdate.
|
||||
if ($dbman->field_exists($table, $field)) {
|
||||
$dbman->drop_field($table, $field);
|
||||
}
|
||||
|
||||
// Define field enddate to be dropped from local_treestudyplan.
|
||||
$table = new xmldb_table('local_treestudyplan');
|
||||
$field = new xmldb_field('enddate');
|
||||
|
||||
// Conditionally launch drop field enddate.
|
||||
if ($dbman->field_exists($table, $field)) {
|
||||
$dbman->drop_field($table, $field);
|
||||
}
|
||||
|
||||
// Treestudyplan savepoint reached.
|
||||
upgrade_plugin_savepoint(true, 2023072000, 'local', 'treestudyplan');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user