Grade xblock from another diagno app - openedx

Is there any way to grade an xblock outside of it, for example, from another edX django application? Or post an answer to it from outside.

I used Submissions for it
from submissions import api
from student.models import anonymous_id_for_user
submission_id = {"item_id": xblock.location,
"item_type": 'xblock',
"course_id": course_id,
"student_id": anonymous_id_for_user(user, course_id)}
submission = api.create_submission(submission_id, {'comment': 'some comment'}, attempt_number=1)
api.set_score(submission['uuid'], grade, weight))
It wasn't exactly what I need but it was the best solution open edX can give.
Pros and cons:
Pros: in progress of the course you'll see grading for that xblock and edx will consider this xblock as passed
Cons: xblock won't consider himself as passed, so when student open it he'll be able to pass it again, but my xblocks are not shown to students so it's not a problem for me

Related

Select Model based on year variable

I am building a new app in Rails for an internal project that changes slightly each year based on the requirements of our clients. Any changes between years will occur within the models (add/remove columns, formatting, reports, etc). My plan is to build it to the requirements for this year and going forward each year I will create a new model and migration (e.g. Sample2019Record, Sample2020Record) that will encapsulate the requirements for that year. The app also needs to render previous year data and all the data is scoped based on the year meaning there is no need to render or query multiple years data. I would prefer not to create a new app each year since that is more apps that need to be maintained.
So my idea is to include the year into the URL (/2018/sample/new or /sample/new?year=2018) and parse the model based on the year ("Sample#{year}Record"). Can rails handle this safely and is there a Gem that can help assist with this approach?
Here is what I came up with, thanks for the advice.
routes.rb
get '/:year/samples', to: 'samples#index', as: :samples, defaults: { year: Time.current.year }
Routes will always default to the current year
application_controller.rb
before_action :check_year
def check_year
if params.has_key?(:year)
if "Sample#{params[:year]}Record".safe_constantize.nil?
redirect_to root_path(year: Time.current.year), notice: "Invalid Year"
end
else
redirect_to root_path(year: Time.current.year), notice: "Invalid Year"
end
end
def get_sample_record(year=Time.current.year)
"Sample#{year}Record".safe_constantize
end
Added a before_action to check the year parameter and added the get_sample_record method to safely constantize the record that can be called from any controller with an optional year like so:
sample_controller.rb
sample_2018_record = get_sample_record
sample_2018_record.count
#> 304
sample_2017_record = get_sample_record 2017
sample_2017_record.count
#> 575683
The result will be nil if an invalid year is passed so I will handle the check in the controller.
As #DaveNewton said, this seems like it should work fine so long as you keep data corresponding to different years' requirements in different tables. A few other observations:
Rails has a helper method constantize for parsing a model from a string:
klass_name = "Sample#{year}Record"
record = klass_name.constantize.new()
will make the variable record an instance of your class corresponding to the year variable. You may find it helpful to use a Factory pattern to encapsulate the process.
Also. be careful how you name and organise your files. You may find this thread helpful when working with the Rails infectors for classes with numbers in their names. A big part of working with Rails is allowing its magic to work for you rather than unwittingly trying to work against it.
As a general rather than Rails-specific piece of advice, I'd also give a considerable amount of thought to how you could define a common public interface for records that will persist across years. A codebase featuring things like
if record.instance_of? Sample2018Record
record.my_2018_method
elsif record.instance_of? Sample2019Record
record.my_method_only_relevant_to_2019
...
will become very difficult to reason about, especially for developers who join after a couple of years. Ruby has extremely powerful tools to help you duck type very effectively.

Pull reports from Shopify

I am trying to pull reports automatically from shopify admin portal. From source page I can see that javascript function makes this call -
var shopifyQL = "**SHOW** quantity_count, total_sales BY product_type, product_title, sku, shipping_city, traffic_source, source, variant_title, host, shipping_country, shipping_province, day, month, referrer FROM **products** SINCE xxx UNTIL yyy ORDER BY total_sales DESC";
var options = {"category":"product_reports","id":wwwwww,"name":"Product Report by SKU","shopify_ql":"SHOW quantity_count, total_sales BY product_type, product_title, sku, shipping_city, traffic_source, source, variant_title, host, shipping_country, shipping_province, day, month, referrer FROM products SINCE xxxx UNTIL yyyy ORDER BY total_sales DESC","updated_at":"zzz"};
However looking at the product API (https://docs.shopify.com/api/product) I do not see most of the attributes. I am assuming some join tables or seperate calls to the model. Also I tried to pull single sku information but it pulls everything.
ShopifyAPI::Product.find(:all, :params => {:variants => {:sku => 'zzzz'}})
Does anybody had any experience to work with reports??
You need to grab the data from the api and play with it. The available objects are clearly stated on the Shopify API docs. Admin dashboard data can't be pulled like the way you seem to envision unless you play with JavaScript injection (tampermonkey...) which is highly not recommended.
It would go like this for you. First off, if you pull products, you have do so in chunks of 250. The :all symbol gives you up to 250. Supplying a page and limit parameter would help there.
Second, you cannot filter by SKU. Instead, download all the products, and then inside each product are the variants. A variant has a SKU, so you'd search that way.
Doing that, you could setup your own nice reference data structure, ready to be used in reporting as you see fit.

Magento SalesOrderList... is there a ligth weight version of this, or a way to trim down the returned value

I am attempting to get all the orders from a magento instance. Once a day we grab all the orders.. (sometimes a few thousand)
Extra stuff that's more why I ask:
I'm using ruby-on-rails to grab the orders. This involves sending the soap call to the magento instance. It's easy as.
Once I have the response, I convert it into a Hash (a tree) and then pick out the increment id's of the orders and proceed to call getOrder with the increment id.
I have two problems with what's going on now, one operational, and one religious.
Grabbing the XML response to the list request takes really really long and when you tack on the work involved in converting the XML to a hash, I'm seeing a really slow processes.
The religious bit is that I just want the increment_ids so why do I have to pay for the processing/bandwidth to support a hugely bloated response.
Ok so the question...
Is there a way to set the response returned from Magento, to include only specific fields? Only the updated_at and the increment_id for instance.
If not, is there another call I'm not aware of, that can get just the increment_ids and date?
Edit
Below is an example of what I'm looking for from magento but it's for ebay. I send this xml up to ebay, and get back a really really specific bit of info about the product. It works for orders and such too. I can say "only this" and get just that. I want the same from Magento
<GetItemRequest xmlns="urn:ebay:apis:eBLBaseComponents">
<SKU>b123-332</SKU><OutputSelector>ItemId</OutputSelector>
</GetItemRequest>
I've created a rubygem that gives you your salesOrderList response in the form of a hash, and you can do what you want with the orders after you've received them back (i.e. select the fields you want including increment_id). Just run
gem install magento_api_wrapper
To do what you want to do, you would do something like this:
api = MagentoApiWrapper::Sales.new(magento_url: "yourmagentostore.com/index.php", magento_username: "soap_api_username", magento_api_key: "userkey123")
orders = api.order_list(simple_filters: [{key: "status" value: "complete"}])
orders.map {|o| [o.increment_id, o.items.first.sku] }
Rough guess, but you get the idea. You would get the array of hashes back and you can do what you want with them after that. Good luck!

Chargify API coupon creation

I am currently working on a project and facing a problem with a task. I am trying to randomly generate a 6 digit coupon number and post it to chargify account via there API. If the coupon creation is successful I want the same coupon code to be send to the customer through Email.
As per chargify documentation this is how I should send all the details to chargify from my application :
{"subscription":{
"product_handle":"[#product.handle]",
"customer_attributes":{
"first_name":"Joe",
"last_name":"Blow",
"email":"joe#example.com"
},
"credit_card_attributes":{
"full_number":"1",
"expiration_month":"10",
"expiration_year":"2020"
},
"coupon_code":"6 digit random code"
}}
"""
https://[#subdomain].chargify.com/subscriptions.json.
I am able to create a 6 digit random numerical code by this method :
rand(999999).to_s.center(6, rand(9).to_s).
However this does not seem to be working for me. Any suggestions would be highly appreciated.
Thanks
I'm not part of our tech or dev staff, but I'm 99% sure you can only specify a previously-defined coupon code in that API call. You must define coupon codes in the Chargify admin web interface. In the API call above, you can apply a coupon to the subscription, but the assumption is that you already defined that coupon code in the admin interface.
We will add that capability in the future, but I don't have a specific date for you.
Sorry about that.
--- Lance Walley
--- Chargify
I'm not sure what you're trying to do with your call to center. The most sensible thing to do would be to zero-fill the coupon code. This would do it:
"%06d" % rand(1000000)
This will generate codes such as "664001" and "061532".
Note that you want rand(1000000) rather than rand(999999). That's because rand gives you random integers between 0 and one less than the argument. rand(999999) will only give you random numbers up to 999998.
There's a violation of DRY (Don't Repeat Yourself) in the above code: Both the "06" and the "1000000" depend upon the length of the coupon code. Here's a fix for that:
COUPON_CODE_LENGTH = 6
"%0#{COUPON_CODE_LENGTH}d" % rand(10 ** COUPON_CODE_LENGTH)
Although longer, there's now only one thing to change if the coupon code length should change. The replacement of "magic numbers" with a named constant also helps the code to communicate its intent.

RESTful nested conventional routing

I have the model:
User -1---n- Transaction(amount,description, date)
User -1---n- TransactionImport -1---n- TransactonImportField(name,value)
(personal expense tracking app).
What I want to achieve is this:
User opens URL and pastes the CSV with the list of transactions.
User submits it.
System extracts data from CSV into TransactionImport (row) + TransactionImportField (cell).
User can choose which column means what (amount, description, date) from the imported data in TransactionImport(Field).
User click save and the system transfers TransactionImport into the Transaction.
What I can't seem to get right is the fact that step 3 creates multiple records of TransactionImport (and related TransactionImportField).
So doing POST /transaction_imports?csv=abcd is expected to produce one record if we would be RESTful. But the code is supposed to be something like this:
# TransactionImportsController
def create
result = TransactionImports.parse(params[:csv])
flash[:notice] = result.message
redirect_to transaction_imports_path
end
I am probably approaching the task from a wrong angle as I feel that implementation doesn't fit in tp the inherited_resources.
Could you please advise what would be the most conventional way of implementing this?
Thanks,
Dmytrii.
REST/HTTP has no expectation that doing POST will only create one record. That maybe the default rails behaviour, but you should not constrain your design because of that.

Resources