Groundwork for pages and periods in backend
This commit is contained in:
parent
ff321e21c5
commit
3687461585
10 changed files with 720 additions and 297 deletions
|
@ -195,7 +195,7 @@ class gradeinfo {
|
||||||
"required" => $this->is_required(),
|
"required" => $this->is_required(),
|
||||||
];
|
];
|
||||||
// Unfortunately, lazy loading of the completion data is off, since we need the data to show study item completion...
|
// 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()){
|
&& $this->gradingscanner->is_available()){
|
||||||
$model['grading'] = $this->gradingscanner->model();
|
$model['grading'] = $this->gradingscanner->model();
|
||||||
}
|
}
|
||||||
|
@ -246,7 +246,7 @@ class gradeinfo {
|
||||||
if(!isset($this->studyitem)){
|
if(!isset($this->studyitem)){
|
||||||
throw new \UnexpectedValueException("Study item not set (null) for gradeinfo in report mode");
|
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);
|
$completion = $aggregator->grade_completion($this,$userid);
|
||||||
|
|
||||||
$model = [
|
$model = [
|
||||||
|
|
|
@ -72,7 +72,7 @@ class tristate_aggregator extends \local_treestudyplan\aggregator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid){
|
public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid){
|
||||||
$condition = $studyitem->getConditions();
|
$condition = $studyitem->conditions();
|
||||||
if(empty($condition)){
|
if(empty($condition)){
|
||||||
$condition = self::DEFAULT_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){
|
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
|
// if null result (conditions are unknown/null) - default to ALL
|
||||||
return isset($completed)?$completed:(self::aggregate_completion($completion,'ALL'));
|
return isset($completed)?$completed:(self::aggregate_completion($completion,'ALL'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,11 +27,11 @@ class studyitem {
|
||||||
return $this->studyline->context();
|
return $this->studyline->context();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStudyline(): studyline {
|
public function studyline(): studyline {
|
||||||
return $this->studyline;
|
return $this->studyline;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getConditions() {
|
public function conditions() {
|
||||||
return $this->r->conditions;
|
return $this->r->conditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ class studyitem {
|
||||||
$this->r = $DB->get_record(self::TABLE,['id' => $id],"*",MUST_EXIST);
|
$this->r = $DB->get_record(self::TABLE,['id' => $id],"*",MUST_EXIST);
|
||||||
|
|
||||||
$this->studyline = studyline::findById($this->r->line_id);
|
$this->studyline = studyline::findById($this->r->line_id);
|
||||||
$this->aggregator = $this->getStudyline()->getStudyplan()->getAggregator();
|
$this->aggregator = $this->studyline()->studyplan()->aggregator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function id(){
|
public function id(){
|
||||||
|
@ -147,7 +147,7 @@ class studyitem {
|
||||||
} else {
|
} else {
|
||||||
// Also supply a list of linked users, so the badgeinfo can give stats on
|
// Also supply a list of linked users, so the badgeinfo can give stats on
|
||||||
// the amount issued, related to this studyplan
|
// 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);
|
$model['badge'] = $badgeinfo->editor_model($studentids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ class studyitem {
|
||||||
$item = self::findById($id);
|
$item = self::findById($id);
|
||||||
if($item->type() == self::COURSE){
|
if($item->type() == self::COURSE){
|
||||||
// Signal the studyplan that a course has been added so it can be marked for csync cascading
|
// 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;
|
return $item;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,14 @@ class studyline {
|
||||||
|
|
||||||
private $r; // Holds database record
|
private $r; // Holds database record
|
||||||
private $id;
|
private $id;
|
||||||
|
private $page;
|
||||||
private $studyplan;
|
private $studyplan;
|
||||||
|
|
||||||
public function context(): \context {
|
public function context(): \context {
|
||||||
return $this->studyplan->context();
|
return $this->studyplan->context();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStudyplan() : studyplan {
|
public function studyplan() : studyplan {
|
||||||
return $this->studyplan;
|
return $this->studyplan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +48,8 @@ class studyline {
|
||||||
global $DB;
|
global $DB;
|
||||||
$this->id = $id;
|
$this->id = $id;
|
||||||
$this->r = $DB->get_record(self::TABLE,['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(){
|
public function id(){
|
||||||
|
@ -105,7 +107,7 @@ class studyline {
|
||||||
// make sure there are enought slots to account for them
|
// 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.
|
// 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]);
|
$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
|
// Create the required amount of slots
|
||||||
for($i=0; $i < $num_slots+1; $i++){
|
for($i=0; $i < $num_slots+1; $i++){
|
||||||
|
@ -150,13 +152,13 @@ class studyline {
|
||||||
public static function add($fields){
|
public static function add($fields){
|
||||||
global $DB;
|
global $DB;
|
||||||
|
|
||||||
if(!isset($fields['studyplan_id'])){
|
if(!isset($fields['page_id'])){
|
||||||
throw new \InvalidArgumentException("parameter 'studyplan_id' missing");
|
throw new \InvalidArgumentException("parameter 'page_id' missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
$studyplan_id = $fields['studyplan_id'];
|
$page_id = $fields['page_id'];
|
||||||
$sqmax = $DB->get_field_select(self::TABLE,"MAX(sequence)","studyplan_id = :studyplan_id",['studyplan_id' => $studyplan_id]);
|
$sqmax = $DB->get_field_select(self::TABLE,"MAX(sequence)","page_id = :page_id",['page_id' => $page_id]);
|
||||||
$addable = ['studyplan_id','name','shortname','color'];
|
$addable = ['page_id','name','shortname','color'];
|
||||||
$info = ['sequence' => $sqmax+1];
|
$info = ['sequence' => $sqmax+1];
|
||||||
foreach($addable as $f){
|
foreach($addable as $f){
|
||||||
if(array_key_exists($f,$fields)){
|
if(array_key_exists($f,$fields)){
|
||||||
|
@ -217,11 +219,12 @@ class studyline {
|
||||||
return success::success();
|
return success::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function find_studyplan_children(studyplan $plan)
|
public static function find_page_children(studyplanpage $page)
|
||||||
{
|
{
|
||||||
global $DB;
|
global $DB;
|
||||||
$list = [];
|
$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) {
|
foreach($ids as $id) {
|
||||||
$list[] = self::findById($id);
|
$list[] = self::findById($id);
|
||||||
}
|
}
|
||||||
|
@ -263,7 +266,7 @@ class studyline {
|
||||||
// make sure there are enought slots to account for them
|
// 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.
|
// 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]);
|
$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
|
// Create the required amount of slots
|
||||||
for($i=0; $i < $num_slots+1; $i++){
|
for($i=0; $i < $num_slots+1; $i++){
|
||||||
|
@ -339,7 +342,7 @@ class studyline {
|
||||||
$filterlayer = 0;
|
$filterlayer = 0;
|
||||||
foreach($slotmodel as $itemmodel)
|
foreach($slotmodel as $itemmodel)
|
||||||
{
|
{
|
||||||
if($itemmodel["type"] == "course" || $itemmodel["type"] == "competency"){
|
if($itemmodel["type"] == "course"){
|
||||||
$itemmodel["layer"] = $courselayer;
|
$itemmodel["layer"] = $courselayer;
|
||||||
$courselayer++;
|
$courselayer++;
|
||||||
}else {
|
}else {
|
||||||
|
|
|
@ -15,8 +15,9 @@ class studyplan {
|
||||||
private $aggregator;
|
private $aggregator;
|
||||||
private $context = null; // Hold context object once retrieved
|
private $context = null; // Hold context object once retrieved
|
||||||
private $linked_userids = null; // cache lookup of linked users (saves queries)
|
private $linked_userids = null; // cache lookup of linked users (saves queries)
|
||||||
|
private $page_cache = null;
|
||||||
|
|
||||||
public function getAggregator(){
|
public function aggregator(){
|
||||||
return $this->aggregator;
|
return $this->aggregator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,10 +45,6 @@ class studyplan {
|
||||||
return $this->r->shortname;
|
return $this->r->shortname;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function slots(){
|
|
||||||
return $this->r->slots;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function name(){
|
public function name(){
|
||||||
return $this->r->name;
|
return $this->r->name;
|
||||||
}
|
}
|
||||||
|
@ -56,6 +53,16 @@ class studyplan {
|
||||||
return new \DateTime($this->r->startdate);
|
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
|
* Return the context this studyplan is associated to
|
||||||
*/
|
*/
|
||||||
|
@ -71,27 +78,13 @@ class studyplan {
|
||||||
return $this->context;
|
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){
|
public static function simple_structure($value=VALUE_REQUIRED){
|
||||||
return new \external_single_structure([
|
return new \external_single_structure([
|
||||||
"id" => new \external_value(PARAM_INT, 'id of studyplan'),
|
"id" => new \external_value(PARAM_INT, 'id of studyplan'),
|
||||||
"name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
|
"name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
|
||||||
"shortname"=> new \external_value(PARAM_TEXT, 'shortname 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'),
|
"context_id" => new \external_value(PARAM_INT, 'context_id of studyplan'),
|
||||||
"description"=> new \external_value(PARAM_TEXT, 'description 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" => new \external_value(PARAM_TEXT, 'selected aggregator'),
|
||||||
"aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator'),
|
"aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator'),
|
||||||
"aggregation_info" => aggregator::basic_structure(),
|
"aggregation_info" => aggregator::basic_structure(),
|
||||||
|
@ -103,11 +96,8 @@ class studyplan {
|
||||||
'id' => $this->r->id,
|
'id' => $this->r->id,
|
||||||
'name' => $this->r->name,
|
'name' => $this->r->name,
|
||||||
'shortname' => $this->r->shortname,
|
'shortname' => $this->r->shortname,
|
||||||
'slots' => $this->r->slots,
|
|
||||||
'context_id' => $this->context()->id,
|
'context_id' => $this->context()->id,
|
||||||
'description' => $this->r->description,
|
'description' => $this->r->description,
|
||||||
'startdate' => $this->r->startdate,
|
|
||||||
'enddate' => $this->r->enddate,
|
|
||||||
"aggregation" => $this->r->aggregation,
|
"aggregation" => $this->r->aggregation,
|
||||||
"aggregation_config" => $this->aggregator->config_string(),
|
"aggregation_config" => $this->aggregator->config_string(),
|
||||||
'aggregation_info' => $this->aggregator->basic_model(),
|
'aggregation_info' => $this->aggregator->basic_model(),
|
||||||
|
@ -120,18 +110,11 @@ class studyplan {
|
||||||
"name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
|
"name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
|
||||||
"shortname"=> new \external_value(PARAM_TEXT, 'shortname of studyplan'),
|
"shortname"=> new \external_value(PARAM_TEXT, 'shortname of studyplan'),
|
||||||
"description"=> new \external_value(PARAM_TEXT, 'description 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'),
|
"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" => new \external_value(PARAM_TEXT, 'selected aggregator'),
|
||||||
"aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator'),
|
"aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator'),
|
||||||
"aggregation_info" => aggregator::basic_structure(),
|
"aggregation_info" => aggregator::basic_structure(),
|
||||||
/*"association" => new \external_single_structure([
|
"pages" => new \external_multiple_structure(studyplanpage::editor_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()),
|
|
||||||
"advanced" => new \external_single_structure([
|
"advanced" => new \external_single_structure([
|
||||||
"force_scales" => new \external_single_structure([
|
"force_scales" => new \external_single_structure([
|
||||||
"scales" => new \external_multiple_structure(new \external_single_structure([
|
"scales" => new \external_multiple_structure(new \external_single_structure([
|
||||||
|
@ -151,24 +134,16 @@ class studyplan {
|
||||||
'name' => $this->r->name,
|
'name' => $this->r->name,
|
||||||
'shortname' => $this->r->shortname,
|
'shortname' => $this->r->shortname,
|
||||||
'description' => $this->r->description,
|
'description' => $this->r->description,
|
||||||
'slots' => $this->r->slots,
|
|
||||||
'context_id' => $this->context()->id,
|
'context_id' => $this->context()->id,
|
||||||
'startdate' => $this->r->startdate,
|
|
||||||
'enddate' => $this->r->enddate,
|
|
||||||
"aggregation" => $this->r->aggregation,
|
"aggregation" => $this->r->aggregation,
|
||||||
"aggregation_config" => $this->aggregator->config_string(),
|
"aggregation_config" => $this->aggregator->config_string(),
|
||||||
'aggregation_info' => $this->aggregator->basic_model(),
|
'aggregation_info' => $this->aggregator->basic_model(),
|
||||||
/*'association' => [
|
'pages' => [],
|
||||||
'cohorts' => associationservice::associated_cohorts($this->r->id),
|
|
||||||
'users' => associationservice::associated_users($this->r->id),
|
|
||||||
],*/
|
|
||||||
'studylines' => [],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$children = studyline::find_studyplan_children($this);
|
foreach($this->pages() as $p)
|
||||||
foreach($children as $c)
|
|
||||||
{
|
{
|
||||||
$model['studylines'][] = $c->editor_model();
|
$model['pages'][] = $p->editor_model();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(has_capability('local/treestudyplan:forcescales', \context_system::instance())){
|
if(has_capability('local/treestudyplan:forcescales', \context_system::instance())){
|
||||||
|
@ -231,14 +206,14 @@ class studyplan {
|
||||||
global $DB;
|
global $DB;
|
||||||
|
|
||||||
if($force){
|
if($force){
|
||||||
$children = studyline::find_studyplan_children($this);
|
$children = studyplanpage::find_studyplan_children($this);
|
||||||
foreach($children as $c){
|
foreach($children as $c){
|
||||||
$c->delete($force);
|
$c->delete($force);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($DB->count_records('local_treestudyplan_line',['studyplan_id' => $this->id]) > 0){
|
if($DB->count_records('local_treestudyplan_page',['studyplan_id' => $this->id]) > 0){
|
||||||
return success::fail('cannot delete studyplan that still has studylines');
|
return success::fail('cannot delete studyplan that still has pages');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -407,10 +382,7 @@ class studyplan {
|
||||||
"name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
|
"name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
|
||||||
"shortname"=> new \external_value(PARAM_TEXT, 'shortname of studyplan'),
|
"shortname"=> new \external_value(PARAM_TEXT, 'shortname of studyplan'),
|
||||||
"description"=> new \external_value(PARAM_TEXT, 'description of studyplan'),
|
"description"=> new \external_value(PARAM_TEXT, 'description of studyplan'),
|
||||||
"slots" => new \external_value(PARAM_INT, 'number of slots in studyplan'),
|
"pages" => new \external_multiple_structure(studyplanpage::user_structure()),
|
||||||
"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()),
|
|
||||||
"aggregation_info" => aggregator::basic_structure(),
|
"aggregation_info" => aggregator::basic_structure(),
|
||||||
],'Studyplan with user info',$value);
|
],'Studyplan with user info',$value);
|
||||||
|
|
||||||
|
@ -423,17 +395,13 @@ class studyplan {
|
||||||
'name' => $this->r->name,
|
'name' => $this->r->name,
|
||||||
'shortname' => $this->r->shortname,
|
'shortname' => $this->r->shortname,
|
||||||
'description' => $this->r->description,
|
'description' => $this->r->description,
|
||||||
'slots' => $this->r->slots,
|
'pages' => [],
|
||||||
'startdate' => $this->r->startdate,
|
|
||||||
'enddate' => $this->r->enddate,
|
|
||||||
'studylines' => [],
|
|
||||||
'aggregation_info' => $this->aggregator->basic_model(),
|
'aggregation_info' => $this->aggregator->basic_model(),
|
||||||
];
|
];
|
||||||
|
|
||||||
$children = studyline::find_studyplan_children($this);
|
foreach($this->pages() as $p)
|
||||||
foreach($children as $c)
|
|
||||||
{
|
{
|
||||||
$model['studylines'][] = $c->user_model($userid);
|
$model['pages'][] = $p->user_model($userid);
|
||||||
}
|
}
|
||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
@ -459,24 +427,10 @@ class studyplan {
|
||||||
|
|
||||||
// next, copy the studylines
|
// next, copy the studylines
|
||||||
|
|
||||||
$children = studyline::find_studyplan_children($this);
|
foreach($this->pages() as $p){
|
||||||
$itemtranslation = [];
|
$newchild = $p->duplicate($this);
|
||||||
$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;
|
return $new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,95 +447,12 @@ class studyplan {
|
||||||
$model = $this->export_model();
|
$model = $this->export_model();
|
||||||
$json = json_encode([
|
$json = json_encode([
|
||||||
"type"=>"studyplan",
|
"type"=>"studyplan",
|
||||||
"version"=>1.0,
|
"version"=>2.0,
|
||||||
"studyplan"=>$model
|
"studyplan"=>$model
|
||||||
],\JSON_PRETTY_PRINT);
|
],\JSON_PRETTY_PRINT);
|
||||||
return [ "format" => "application/json", "content" => $json];
|
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()
|
public function export_model()
|
||||||
{
|
{
|
||||||
$model = [
|
$model = [
|
||||||
|
@ -594,20 +465,19 @@ class studyplan {
|
||||||
"aggregation" => $this->r->aggregation,
|
"aggregation" => $this->r->aggregation,
|
||||||
"aggregation_config" => json_decode($this->aggregator->config_string()),
|
"aggregation_config" => json_decode($this->aggregator->config_string()),
|
||||||
'aggregation_info' => $this->aggregator->basic_model(),
|
'aggregation_info' => $this->aggregator->basic_model(),
|
||||||
'studylines' => $this->export_studylines_model(),
|
'pages' => $this->export_pages_model(),
|
||||||
];
|
];
|
||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function export_studylines_model()
|
public function export_pages_model()
|
||||||
{
|
{
|
||||||
$children = studyline::find_studyplan_children($this);
|
$pages = [];
|
||||||
$lines = [];
|
foreach($this->pages() as $p)
|
||||||
foreach($children as $c)
|
|
||||||
{
|
{
|
||||||
$lines[] = $c->export_model();
|
$pages[] = $p->export_model();
|
||||||
}
|
}
|
||||||
return $lines;
|
return $pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function import_studyplan($content,$format="application/json",$context_id=1)
|
public static function import_studyplan($content,$format="application/json",$context_id=1)
|
||||||
|
@ -615,15 +485,18 @@ class studyplan {
|
||||||
if($format != "application/json") { return false;}
|
if($format != "application/json") { return false;}
|
||||||
|
|
||||||
$content = json_decode($content,true);
|
$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
|
// Make sure the aggregation_config is re-encoded as json text
|
||||||
$content["studyplan"]["aggregation_config"] = json_encode($content["studyplan"]["aggregation_config"]);
|
$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;
|
$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"]);
|
$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 {
|
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;}
|
if($format != "application/json") { return false;}
|
||||||
$content = json_decode($content,true);
|
$content = json_decode($content,true);
|
||||||
if($content["type"] == "studylines" && $content["version"] >= 1.0){
|
if($content["version"] >= 2.0){
|
||||||
return $this->import_studylines_model($content["studylines"]);
|
if($content["type"] == "studyplanpage"){
|
||||||
}
|
// import single page from a studyplanpage (wrapped in array of one page)
|
||||||
else if($content["type"] == "studyplan" && $content["version"] >= 1.0){
|
return $this->import_pages_model([$content["page"]]);
|
||||||
return $this->import_studylines_model($content["studyplan"]["studylines"]);
|
}
|
||||||
|
else if($content["type"] == "studyplan"){
|
||||||
|
// Import all pages from the studyplan
|
||||||
|
return $this->import_pages_model($content["studyplan"]["pages"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function find_studyline_by_shortname($shortname){
|
protected function import_pages_model($model)
|
||||||
$children = studyline::find_studyplan_children($this);
|
|
||||||
foreach($children as $l){
|
|
||||||
if($shortname == $l->shortname()){
|
|
||||||
return $l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function import_studylines_model($model)
|
|
||||||
{
|
{
|
||||||
// First attempt to map each studyline model to an existing or new line
|
$this->pages(); // make sure the page cache is initialized, since we will be adding to it.
|
||||||
$line_map = [];
|
foreach($model as $p){
|
||||||
foreach($model as $ix => $linemodel){
|
$p["studyplan_id"] = $this->id();
|
||||||
$line = $this->find_studyline_by_shortname($linemodel["shortname"]);
|
$page = studyplanpage::add($p);
|
||||||
if(empty($line)){
|
$this->page_cache[] = $page;
|
||||||
$linemodel["studyplan_id"] = $this->id;
|
$page->import_studylines_model($p["studylines"]);
|
||||||
$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;
|
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}");
|
cli_writeln(" - {$u->firstname} {$u->lastname} / {$u->username}");
|
||||||
}
|
}
|
||||||
|
|
||||||
$lines = studyline::find_studyplan_children($plan);
|
foreach($plan->pages() as $page){
|
||||||
foreach($lines as $line){
|
$lines = studyline::find_page_children($page);
|
||||||
cli_writeln(" ** {$line->name()} **");
|
foreach($lines as $line){
|
||||||
$items = studyitem::find_studyline_children($line);
|
cli_writeln(" ** {$line->name()} **");
|
||||||
foreach($users as $u){
|
$items = studyitem::find_studyline_children($line);
|
||||||
$generator->addskill($u->username,$line->shortname());
|
foreach($users as $u){
|
||||||
|
$generator->addskill($u->username,$line->shortname());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,74 +89,76 @@ foreach($plans as $plan){
|
||||||
cli_heading($plan->name());
|
cli_heading($plan->name());
|
||||||
$users = $plan->find_linked_users();
|
$users = $plan->find_linked_users();
|
||||||
|
|
||||||
$lines = studyline::find_studyplan_children($plan);
|
foreach($plan->pages() as $page){
|
||||||
foreach($lines as $line){
|
$lines = studyline::find_page_children($page);
|
||||||
cli_writeln(" ** {$line->name()} **");
|
foreach($lines as $line){
|
||||||
$items = studyitem::find_studyline_children($line);
|
cli_writeln(" ** {$line->name()} **");
|
||||||
foreach($items as $item){
|
$items = studyitem::find_studyline_children($line);
|
||||||
if($item->type() == studyitem::COURSE) { // only handle courses for now
|
foreach($items as $item){
|
||||||
$courseinfo = $item->getcourseinfo();
|
if($item->type() == studyitem::COURSE) { // only handle courses for now
|
||||||
cli_writeln(" # {$courseinfo->shortname()}");
|
$courseinfo = $item->getcourseinfo();
|
||||||
|
cli_writeln(" # {$courseinfo->shortname()}");
|
||||||
|
|
||||||
if($courseinfo->course()->startdate <= time()){
|
if($courseinfo->course()->startdate <= time()){
|
||||||
|
|
||||||
foreach($users as $u){
|
foreach($users as $u){
|
||||||
cli_writeln(" -> {$u->firstname} {$u->lastname} <-");
|
cli_writeln(" -> {$u->firstname} {$u->lastname} <-");
|
||||||
$gradables = gradeinfo::list_studyitem_gradables($item);
|
$gradables = gradeinfo::list_studyitem_gradables($item);
|
||||||
$gen = $generator->generate($u->username,$line->shortname(),$gradables);
|
$gen = $generator->generate($u->username,$line->shortname(),$gradables);
|
||||||
foreach($gen as $gg){
|
foreach($gen as $gg){
|
||||||
$g = $gg->gi;
|
$g = $gg->gi;
|
||||||
$gi = $g->getGradeitem();
|
$gi = $g->getGradeitem();
|
||||||
|
|
||||||
$name = $gi->itemname;
|
$name = $gi->itemname;
|
||||||
$grade = $gg->gradetext;
|
$grade = $gg->gradetext;
|
||||||
cli_write (" - {$name} = {$grade}");
|
cli_write (" - {$name} = {$grade}");
|
||||||
|
|
||||||
// Check if the item is alreaady graded for this user
|
// 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',
|
$existing = $count = $DB->count_records_select('grade_grades','itemid = :gradeitemid AND finalgrade IS NOT NULL and userid = :userid',
|
||||||
['gradeitemid' => $gi->id, 'userid' => $u->id]);
|
['gradeitemid' => $gi->id, 'userid' => $u->id]);
|
||||||
|
|
||||||
if(!$existing){
|
if(!$existing){
|
||||||
if($gg->grade > 0){
|
if($gg->grade > 0){
|
||||||
if($gi->itemmodule == "assign"){
|
if($gi->itemmodule == "assign"){
|
||||||
// If it is an assignment, submit though that interface
|
// If it is an assignment, submit though that interface
|
||||||
list($c,$cminfo) = get_course_and_cm_from_instance($gi->iteminstance,$gi->itemmodule);
|
list($c,$cminfo) = get_course_and_cm_from_instance($gi->iteminstance,$gi->itemmodule);
|
||||||
$cm_ctx = \context_module::instance($cminfo->id);
|
$cm_ctx = \context_module::instance($cminfo->id);
|
||||||
$a = new \assign($cm_ctx,$cminfo,$c);
|
$a = new \assign($cm_ctx,$cminfo,$c);
|
||||||
|
|
||||||
$ug = $a->get_user_grade($u->id,true);
|
$ug = $a->get_user_grade($u->id,true);
|
||||||
$ug->grade = grade_floatval($gg->grade);
|
$ug->grade = grade_floatval($gg->grade);
|
||||||
$ug->grader = $USER->id;
|
$ug->grader = $USER->id;
|
||||||
$ug->feedbacktext = nl2br( htmlspecialchars($gg->fb));
|
$ug->feedbacktext = nl2br( htmlspecialchars($gg->fb));
|
||||||
$ug->feedbackformat = FORMAT_HTML;
|
$ug->feedbackformat = FORMAT_HTML;
|
||||||
|
|
||||||
//print_r($ug);
|
//print_r($ug);
|
||||||
if(!$options["dryrun"]){
|
if(!$options["dryrun"]){
|
||||||
$a->update_grade($ug);
|
$a->update_grade($ug);
|
||||||
|
|
||||||
|
grade_regrade_final_grades($c->id,$u->id,$gi);
|
||||||
|
cli_writeln(" ... Stored");
|
||||||
|
} else {
|
||||||
|
cli_writeln(" ... (Dry Run)");
|
||||||
|
}
|
||||||
|
|
||||||
grade_regrade_final_grades($c->id,$u->id,$gi);
|
|
||||||
cli_writeln(" ... Stored");
|
|
||||||
} else {
|
} else {
|
||||||
cli_writeln(" ... (Dry Run)");
|
// Otherwise, set the grade through the manual grading override
|
||||||
|
cli_writeln(" ... Cannot store");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, set the grade through the manual grading override
|
cli_writeln(" ... No grade");
|
||||||
cli_writeln(" ... Cannot store");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cli_writeln(" ... No grade");
|
cli_writeln(" ... Already graded");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
cli_writeln(" ... Already graded");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
cli_writeln(" Skipping since it has not started yet");
|
||||||
cli_writeln(" Skipping since it has not started yet");
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?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"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
|
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
|
||||||
>
|
>
|
||||||
|
@ -27,9 +27,6 @@
|
||||||
<FIELD NAME="name" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
|
<FIELD NAME="name" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
|
||||||
<FIELD NAME="shortname" 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="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" TYPE="char" LENGTH="30" NOTNULL="true" DEFAULT="bistate" SEQUENCE="false"/>
|
||||||
<FIELD NAME="aggregation_config" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
<FIELD NAME="aggregation_config" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
|
||||||
<FIELD NAME="context_id" TYPE="int" LENGTH="10" 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();
|
$recordset->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Treestudyplan savepoint reached.
|
// Treestudyplan savepoint reached.
|
||||||
upgrade_plugin_savepoint(true, 2023071900, 'local', 'treestudyplan');
|
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 a new issue