How to get a list of all pending assignments under all courses in Moodle using the Web service API - moodle

I want to get a list of all assignments along with their completion status under all courses. Basically what is shown under the timeline in the dashboard.
The WS function core_course_get_enrolled_courses_by_timeline_classification, gives me all the current courses the student is enrolled in when I use the parameter ?classification=inprogress.
Using the course ids from the above function, I pass them as parameters to the function mod_assign_get_assignments, but there's just one tiny problem, it doesn't give any information on whether that assignment has been marked as complete by the student (completion status).
The function core_course_get_contents is a lot more than what I need since it provides every single module in that course and all activities under each module, moreover it gives all this only for one course at a time, however it does give the completion status for each activity.
Also, it would help me out a lot if you provide the required and optional query parameters since the API docs are quite terrible and doesn't provide any of that, I had to google each function which was very time consuming.

The closest function I can see is core_completion_get_activities_completion_status
You can pass the course id and the user id
Which returns with a list of activities including
'cmid' => new external_value(PARAM_INT, 'course module ID'),
'modname' => new external_value(PARAM_PLUGIN, 'activity module name'),
'instance' => new external_value(PARAM_INT, 'instance ID'),
'state' => new external_value(PARAM_INT,
"Completion state value:
0 means incomplete,
1 complete,
2 complete pass,
3 complete fail"
),
'timecompleted' => new external_value(PARAM_INT,
'timestamp for completed activity'),
So in your code, filter the result for modname = 'assign'
The id in the mdl_assign table is the instance id

Related

Running into an issue with a mutation and component flickering with react-query

So, I am making a query everything my context API is updated via a form selection update..
So, order of operation is like so.
User makes a change to a form by selecting (one of possible many) from dropdown.
Change updates "context api" which resaturates the parent component.
Because the form key/values changed, I fire a mutation.
Mutation returns a value. So far, great.
But, when I repeat step #1 - #4, another component flickers with that updated value because at some point the "const" that is expecting a value is undefined... THEN, it has a value..
So, like so..
has a value...
...query api call...
has no value
...returns query
has a value
const ProductPage = (props) => {
const { question } = useContextStateWhatever();
/* Queries */
const { data = {}, isFetched } = useProductUpdatePrice({ questions });
const value = derivePriceFromResponse(data.products);
return (
<SomeComponentRendered value={value} />
)
So, you can see between the "old value" and request in query, that the passed "value" will be undefined. Then query returns, updated value.
I was hoping the query will return any previous value, but the "queryKey" changes with every selection of the form. Deep queryKey.
I was hoping I wouldn't have to then put this value into local state from within a useEffect, or use useRef and create hook to hand back "previous" value until new value is ready.... That's not what react-query is for, right? I mean, shouldn't I be able to make a query call whenever the "context api" changes, and not expect this latency diff of undefined. Any strategies to over come this?
Since the "queryKey" is different (mostly for normal form interaction) for each query, I can see how it can't hand back a previous value until it resolves etc.. any ideas?
Any thoughts?
I think the keepPreviousData: true option is what you are looking for. If the query key changes, you will the get the data from the previous query key, along with an isPreviousData: true flag. The background update will still happen, and then you’ll get the data for the new query key once it arrives. The query will stay in isSuccess status the whole time.

New Relic: How to replace createTracer with startSegment

I had a createTracer() method in the code which we were using in old new relic version.
NR.createTracer("processThread", _ => this._initialize())();
To migrate new relic to latest versin of New Relic, I've replaced it to startSegment() method as per new relic doc: https://github.com/newrelic/node-newrelic/blob/master/Migration%20Guide.md#upgrading-to-agent-v5
NR.startSegment("processThread", _ => this._initialize())();
But now, my code is giving me this error:
TypeError: NR.startSegment(...) is not a function
What am I doing wrong here?
I see the arguments have been changed in startSegment() and createTracer()
https://newrelic.github.io/node-newrelic/docs/API.html#createTracer
As docs suggest, you have a mandatory (boolean) second parameter which you must supply:
NR.startSegment('mySegment', false, _ => this._initialize())();
record bool
Indicates if the segment should be recorded as a metric. Metrics will show up on the transaction breakdown table and server breakdown graph. Segments just show up in transaction traces.
And parameter callback is now optional, you should use third parameter handler for callback
To instrument individual callbacks, call startSegment() inside the callback, and move the main callback logic to the handler function.

Given a Symfony 2.7 formType object with an added choice field, how can I access the choice strings?

I have created a form object in symfony, which is meant to process an api request. It is created in a controller via:
$myForm = new MyFormType($foo, $bar);
$form = $this->createForm($myForm, $entity, array('method' => 'POST', 'csrf_protection' => false));
Once these objects are created, I pass them into an external service which converts api data into data that's meant to be consumed by the form, like the following:
//I pass this in, so that, our api users don't have to pass in
//{uglyAPIVariable45: 721}, but rather {pretty_variable: "go left"}
$goodData = $this->convertOldData($form->getBuilderDataCopy(), $oldData);
One of the things this service does, is if the form has a choice, like a select tag, it attempts to convert any string passed to the api into the value in select tag. This way, if the user can pass over "East" if the select field has the options "North", "West", "South" and "East" associated with the values 0, 4, 5 and 10 and the converter should return 10. The user doesn't have to pass over one of those random values, they can just pass over the string. And if they passed over "Foo", it would return an error along with a description of what good choices they could pass over.
My problem is that, in production, I'm not able to get ahold of those values.
In development, I found that I could get my options using,
foreach($formBuilderDataCopy as $formElement){
$options = $formElement->getAttributes()['data_collector/passed_options']['choices'];
}
But when we moved to production, this code broke. The method, getAttributes, returned null suddenly. Admittedly, it seemed kind of janky to begin with, so I wanted to find a more valid approach to getting these choices, possibly even allowing me to bypass a different approach for 'choice', 'entity', 'timezone' et. al.
Unfortunately, looking through the source code and exploring it with get_class_methods and friends hasn't provided me with a solution this issue. Even when I get choices, the most I can get are values, but not the string options in the select box.
What's the appropriate approach to acquiring such information from my symphony forms?
As a side note, I have also tried:
$form->get('elementName')->getConfig()->getOption('choices');
But it also just returns a list of the ids to single numbers and not the strings that replace those numbers. e.g.
{3: 0, 5: 1, 10: 2, 11: 3...}
Create an entity Object for the form type. Then you can use the data as object. It must not been a doctrine entity.

Exception in Expression Trees

This is my model:
- Business
- BusinesType - FK
- Categories (*) - FK
- Branch (*)
- BranchType - FK
- Address
- Phone (*)
- CustomFields (*)
- OpeningTimes (*)
- WorkingPeriods (*)
- .....
Now I have a controller-action that accepts a form that consists of the whole bunch of data as a single Business entity with all its properties and collections set fine.
Now I have to walk thru all the properties and collections recursively, and compare with the database graph; if they don't exist, add them, if they do walk thru all properties again and perform the same to a deeper level until no navigation properties are left over. Since I have many more properties and descendants than mentioned in the previous example, it's just inside to walk thru them manually.
Thanks to this answer I found GraphDiff which offered a brilliant solution to the situation.
Here's an update query I'm calling:
Context.UpdateGraph(business, bus => bus
.AssociatedEntity(bu => bu.BusinessType)
.AssociatedCollection(bu => bu.Categories)
.OwnedCollection(bu => bu.Branches, branch => branch
.AssociatedEntity(b => b.BranchType)
.OwnedEntity(b => b.Address)
.OwnedCollection(b => b.Phones)
.OwnedCollection(b => b.CustomFields)
.OwnedCollection(b => b.OpeningTimes, openingTimes => openingTimes
.OwnedCollection(b => b.WorkingPeriods)
)
)
);
It throws this exception:
System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.MemberExpression'.
I tried debugging the source code, but I'm not an expert with Expression Trees, the problem occurs when the internal Include call (to include object graph to load store object) tries to attach WorkingPeriods, looks like it's not ready to take that deepness level of recursion. I messed around with it a bit, but I'm sure someone with extensive knowledge in expression trees will be able to solve this easily. Any suggestions will be appreciated on that to.
Here's what the include path expression is supposed to be generated like:
.Include(b =>
b.Branches.Select(br =>
br.OpeningTimes.Select(ot =>
ot.WorkingPeriods)));
Here's the stacktrace of the error.
Essentially, the exception is thrown because the recursive call returns the inner include as a method call, without processing it and returning the collection property it's meant to expose.
sorry it took me a while to get back to you.
I'ts 3am and I've had a fair bit of wine but the problem is fixed :) If you get the latest version of code # https://github.com/refactorthis/GraphDiff it should work fine.
I'll update the new nuget package (RefactorThis.GraphDiff) soon.

Add ‘subscription_date’ and ‘user_ip_address’ fields for Newsletter Subscriber and show it in admin panel in Magento

I want to add two custom fields for newsletter subscriber at which date customer subscribed and its ip address:
1) I had added two columns in ‘newsletter_subscriber’ table.
How this can be achieved?
In the file app/code/core/Mage/Newsletter/Model/Subscriber.php
I found the the functions like:
$this->setSubscriberEmail($email);
$this->setStoreId(Mage::app()->getStore()->getId());
$this->setCustomerId(0);
But i did not found its code.
I think I also save data like that but how it will be possible ? where I have to define and declare code for the function like $this->seSubscriptionDate();
And how to display in Admin panel under Newsletter->Newsletter Subscriber ?
I had not find the solution or help from any person.
I bang my head and find the solution its very simple hope in future may some one’s time save by this post:
1) Add columns in the table “newsletter_subscriber” say in my case “subscription_date” and “sub_ip_address”
2) Add following two lines in the file # two places
app\code\core\Mage\Newsletter\Model\Subscriber.php
**$this->setSubscriptionDate(date("d-m-Y"));
**$this->setSubIpAddress($_SERVER['REMOTE_ADDR']);
One place: in the function: public function subscribe($email)
Before: $this->save();
Second place: in the function: public function subscribeCustomer($customer)
Before: $this->save();
Now data will be added in the table
Now to show in admin panel
1) Open the file app\code\core\Mage\Adminhtml\Block\Newsletter\Subscriber\Grid.php
Just add two required columns in it like
$this->addColumn('subscription_date', array(
'header' => Mage::helper('newsletter')->__('Subscription Date'),
'index' => 'subscription_date',
'default' => '----'
));
$this->addColumn('sub_ip_address', array(
'header' => Mage::helper('newsletter')->__('IP Address'),
'index' => 'sub_ip_address',
'default' => '----'
));
Now finish.
** This is the point where I spent my time and at the end I added this function on hit and trieal basis but it works.
Someone from Core magento team plz explain why this {setSubscriptionDate()}function work ? I had not declared anybody of this function.
It seems it shows intellisense to detect table field?
I also had the similar problem. However, in my case I am supposed to add a "country" and "gender" field in the newsletter form. So, on digging into the core for hours I figured this out. Below is the explaination:
In the app/design/frontend//default/template/newsletter/subscribe.phtml,
the form has getFormActionUrl() ?> in its action field. This referes to app/code/core/Mage/Newsletter/Block/Subscribe.php
The $this object used in the subscirbe.phtml file referes to this class (i.e. Mage_Newsletter_Block_Subscribe) and thus $this->getFormActionUrl() in the subscribe.phtml file refers to the function getFormActionUrl() of this class.
Now, this getFormActionUrl() is returning a method $this->getUrl('newsletter/subscriber/new', array('_secure' => true)) which belongs to its parent Mage_Core_Block_Template (I mean the method getURL()). This part is not improtant to us.
See the parameter passed to the getURL() function which is newsletter/subscriber/new
The first part "newsletter" is the module name (app/code/code/Mage/newsletter), the second part "subscriber" is the controller name (app/code/code/Mage/newsletter/controllers/SubscriberController) and the third part "new" is the method newAction in the controller SubscriberController.
Controller names are suffixed with Controller and function names are suffixed by Action. (Thanks to Phalcon framework to help understand this)
Now in the newAction() method you can see that by default, only the email is being posted
as
$email = (string) $this->getRequest()->getPost('email');
What I did was, I cloned the subscribe.phtml template with a name custom-newsletter.phtml and add two fields with name "country" and "gender". Then added the following lines in the newAction():
$country = (string) $this->getRequest()->getPost('country');
$gender = (string) $this->getRequest()->getPost('gender');
Now at line number 67 of the SubscriberController (inside the newAction() method) there is a code :
$status = Mage::getModel('newsletter/subscriber')->subscribe($email);
This line is calling the subscribe method of app/code/code/Mage/newsletter/Model/Subscriber.php and is passing the $email that is posted from newsletter form into it. I modified the line as:
$status = Mage::getModel('newsletter/subscriber')->subscribe($email,$country,$gender);
Now, I have to edit the app/code/code/Mage/newsletter/Model/Subscriber.php
When we talk about Models, we are talking about the database table the Model refers to. The model name is Subscriber and it belongs to module Newsletter so, the database table that this model affects is newsletter_subscriber
From this part on #Hassan Ali Sshahzad's question will be answered.
I added two new columns there with the name: subscriber_country and subscriber_gender.
Now, Magento' system automatically makes available a getter and a setter function to these columns with the following name:
function getSubscriberCountry(){}
function setSubscriberCountry($country_name){}
So, all I had to do in the model was:
Edit the method subscribe($email) to subscribe($email,$country,$gender)
add the following code in the subscribe($email,$country,$gender) function right before the try statement as follows:
$this->setSubscriberCountry($country);
$this->setSubscriberGender($gender);
try{
$this->save()
Hassan's method played a key role in my understanding of the MVC of Magento so its a return from my side.