How do I get dimensions from a CloudinaryPreloadedFile - cloudinary

I need to save the dimensions of my images in my database to help me render images in a pinterest style gallery format.
I use to have this method:
def update_asset_attributes
if image.present? && image_changed?
ap image.file
self.image_content_type = image.file.content_type
self.image_file_size = image.file.size
self.image_width, self.image_height = `identify -format "%wx%h" #{image.file.path}`.split(/x/)
end
end
But now it says: NoMethodError - undefined method content_type for #<Cloudinary::CarrierWave::PreloadedCloudinaryFile:0x007f9834d81840>:

CloudinaryPreloadedFile doesn't have this information at the moment. You can either -
Pass the information by yourself from the javascript code to the server (you can use the cloudinarydone callback data.result object).
Use the attachinary gem.
If the number of images uploaded per hour are small, you can use the Admin API to get the resource's information given it's public_id.

Related

GridFs read PDF

I am trying to build a financial dashboard with Flask and pymongo. The starting point is a flask form which saves data in a MongoDB database. One of the fields in the form is a FileField (wtforms) which allows the upload of a PDF, which is then stored in MongoDB with GridFS.
Now I manage to save the pdf and I can see the resulting entries within the .files and .chunks collections. Now I would like to build a function that retrieves the PDFs and analyses them with some basic NLP, however I struggle with the getting meaningful data.
When I do:
storage = gridfs.GridFS(db, collection)
data = storage.get('some id')
a = data.read()
The result is a binary file. If I continue with:
with open(data, 'rb') as f:
b = f.read()
The result is "ValueError: embedded null byte or sometimes an empty "byte string".
Any help on this?
To follow up on the above, I found a solution for myself that consists in 2 separate functions:
(1) Upon upload of the form and before uploading the files to MongoDB, I apply a function based on pdfminer that extracts the string content of the PDF and tranform it into a list of sentences using NLTK. I will then store this list in the .files via the storage.put(file, sent_list = sent_list) #sent_list being the variable name of the list of sentences.
Whenever I wish to run NLP operations on the file, I will just call the "sent_list" variable from mongodb.
(2) If I wish to display the stored pdf in its original content however, I included the following function as a separate route.
storage = GridFS(db, collection)
data = storage.get_last_version(filename)
response = make_response(data.read())
extension = data.filename.split('.')[-1]
response.headers['Content-Type'] = f'application/{extension}'
response.headers['Content-Disposition'] = f'inline; filename={data.filename}'
return response
(2) will open a new tab in my flask app showing the .pdf file in its original format.
I hope this helps anyone coming across a similar problem in the future.

GET API call returns non-text content

I am making an API call and reading the response in JSON
r = requests.get(query_url, headers=headers)
pprint(r.json())
But the 'content' is not in text format
'content': 'CmRlbGV0ZSBmcm9tICB7eyBwYXJhbXMuY3VyYXRlZF9kYXRhYmFzZV9uYW1l\n'
'IH19LmNybS5BRkZJTElBVEVfUFJJQ0lORzsKCklOU0VSVCBJTlRPICB7eyBw\n'
'TkcKICB3aGVyZSAxID0gMQogIAogIDsKICAKICAKCg==\n'
How do I convert the 'content' to text
For full context, I am trying to download code from the GitHub repo as text to store in our Database
Are you using python and in particular the requests package? If so, I think you could use r.text.
docs: https://pypi.org/project/requests/
Thanks to the article #CherryDT pointed me to, I was able to get the text
import base64
r_dict = r.json()
content = r_dict['content']
base64.b64decode(content)

Fetch All Pull-Request Comments Via Bitbucket REST API

This is how retrieve a particular pull-request's comments according to bitbucket's documentation:
While I do have the pull-request ID and format a correct URL I still get a 400 response error. I am able to make a POST request to comment but I cannot make a GET. After further reading I noticed the six parameters listed for this endpoint do not say 'optional'. It looks like these need to be supplied in order to retrieve all the comments.
But what exactly are these parameters? I don't find their descriptions to be helpful in the slightest. Any and all help would be greatly appreciated!
fromHash and toHash are only required if diffType is'nt set to EFFECTIVE. state also seems optional to me (didn't give me an error when not including it), and anchorState specifies which kind of comments to fetch - you'd probably want ALL there. As far as I understand it, path contains the path of the file to read comments from. (ex: src/a.py and src/b.py were changed -> specify which of them to fetch comments for)
However, that's probably not what you want. I'm assuming you want to fetch all comments.
You can do that via /rest/api/1.0/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/activities which also includes other activities like reviews, so you'll have to do some filtering.
I won't paste example data from the documentation or the bitbucket instance I tested this once since the json response is quite long. As I've said, there is an example response on the linked page. I also think you'll figure out how to get to the data you want once downloaded since this is a Q&A forum and not a "program this for me" page :b
As a small quickstart: you can use curl like this
curl -u <your_username>:<your_password> https://<bitbucket-url>/rest/api/1.0/projects/<project-key>/repos/<repo-name>/pull-requests/<pr-id>/activities
which will print the response json.
Python version of that curl snippet using the requests module:
import requests
url = "<your-url>" # see above on how to assemble your url
r = requests.get(
url,
params={}, # you'll need this later
auth=requests.auth.HTTPBasicAuth("your-username", "your-password")
)
Note that the result is paginated according to the api documentation, so you'll have to do some extra work to build a full list: Either set an obnoxiously high limit (dirty) or keep making requests until you've fetched everything. I stronly recommend the latter.
You can control which data you get using the start and limit parameters which you can either append to the url directly (e.g. https://bla/asdasdasd/activity?start=25) or - more cleanly - add to the params dict like so:
requests.get(
url,
params={
"start": 25,
"limit": 123
}
)
Putting it all together:
def get_all_pr_activity(url):
start = 0
values = []
while True:
r = requests.get(url, params={
"limit": 10, # adjust this limit to you liking - 10 is probably too low
"start": start
}, auth=requests.auth.HTTPBasicAuth("your-username", "your-password"))
values.extend(r.json()["values"])
if r.json()["isLastPage"]:
return values
start = r.json()["nextPageStart"]
print([x["id"] for x in get_all_pr_activity("my-bitbucket-url")])
will print a list of activity ids, e.g. [77190, 77188, 77123, 77136] and so on. Of course, you should probably not hardcode your username and password there - it's just meant as an example, not production-ready code.
Finally, to filter by action inside the function, you can replace the return values with something like
return [activity for activity in values if activity["action"] == "COMMENTED"]

Odoo v12 API get invoice PDF

This question got me started with my C# Odoo API implementation. I have working code using CookComputing.XmlRpcV2 to retrieve a list of invoices.
What I would like to implement is the option to retrieve/download a PDF of a selected invoice. Does anybody know what I would need to get this done?
When I search, I find forum posts stating reporting doesn't work since V11, such as this one. Neither do I see it mentioned in the online documentation for V12, although there is mention of it at the bottom of the page for V10.
Update
Someone mentioned to construct an URL:
http://localhost:8069/my/invoices/1?report_type=pdf&download=true&access_token=<ACCESSTOKEN>
Where 1 is the invoice ID. Technically this works, but requires me to be logged in to the portal using the browser. Even if I can log into the portal from my C# service, I do not know where/how to retrieve the correct access token. I can see this is in GUID form. Does anybody know whether this is the same token I could retrieve from the OAuth2 REST API (which is a paid module b.t.w.)?
Correct. You can download the PDF by placing in the access_token.
This is the only way I managed to figure it out for Odoo v.12. after smashing my head against a brick wall repeatedly. My example programming language is with Python 3 though, not C#, but I'm sure you can adapt it.
odoo_url_host = "https://company.odoo.com"
The access_token can be found in the invoice's JSON response.
invoice_id = 1234
models = xmlrpcclient.ServerProxy('{}/xmlrpc/2/object'.format(odoo_url_host))
invoice = models.execute_kw(db, uid, password, "account.invoice", read, [[invoice_id]])
which, provided you get back a found invoice, you can use the response like so:
print(invoice["access_token"])
download_url = "%s/%s/my/invoices/%d?report_type=pdf&download=true&access_token=%s" % (odoo_url_host, invoice_id, invoice["access_token"])
Which if you simply want to automatically download, can be done like so:
import urllib.request
pdf_destination = "./invoices/invoice-%d.pdf" % invoice_id
urllib.request.urlretrieve(download_url, pdf_destination)
You'd need to change the way this is written for Python 2.7.
Also, make sure that you click 'share' on the invoice (within odoo) as sometimes the access_token doesn't get generated for that invoice and returns false otherwise.
Or if you'd like to seamlessly have the access_token generated, execute this before trying to get the access token:
ctx = {'active_model': 'account.invoice', 'active_id': invoice_id}
print(models.execute_kw(db, uid, password, 'portal.share', 'default_get',[{}],{'context': ctx}))
That should get you the share_link for the entire document, but all you need is the access_token to be generated. You can extract the access_token from the share_link value in the JSON response, if you'd like to do it that way. Anyway :) Happy coding.
I would try to use /xmlrpc/2/object with model ir.actions.report and method render_qweb_pdf. Bear in mind that you'll need a ir.actions.report record, because render_qweb_pdf isn't a "model method" (in OOP class method).
I am currently testing a similar functionality but with stock.picking where I need delivery form downloaded from a remote Odoo instance and saved as an attachment in another instance. What I did is adding this function to stock.picking in remote Odoo.
#api.model
def sd_get_delivery_form(self, uid):
picking = self.env['stock.picking'].sudo().search([('sd_uid', 'like', uid)], limit=1)
if picking and picking.sale_id:
pdf = self.env.ref('sale.action_report_saleorder').sudo().render_qweb_pdf([picking.sale_id.id])
b64_pdf = base64.b64encode(pdf[0])
order_pdf = b64_pdf.decode('utf-8')
return {'file': order_pdf}
else:
return False
Then calling it using xmlrpc and save it as an attachment
attachment_name = "delivery_order_({})".format(self.id)
self.env['ir.attachment'].create({
'name': attachment_name,
'type': 'binary',
'datas': dt['file'],
'datas_fname': attachment_name + '.pdf',
'store_fname': attachment_name,
'res_model': self._name,
'res_id': self.id,
'mimetype': 'application/x-pdf'
})
What you can do in C# is getting the base64 and save it as PDF if you like to go in this similar way.
The simplest way is to use OdooRPC library : https://pythonhosted.org/OdooRPC/ref_report.html

How can I store extra data with a validation error message in Rails 3?

I'm trying to store some additional data along with the standard error message in a custom validator in Rails 3.
For example, (ignoring built-in validators) suppose I wanted to check to see if a post is a duplicate before it's saved. I might write my custom validation method like this:
class Post < ActiveRecord::Base
# prevent duplicate posts
validate do |post|
duplicates = Post.find_all_by_body(body)
errors.add_to_base("Post is a duplicate!") if duplicates.length
# something like this is desired:
# errors.add_to_base("Post is a duplicate",
# :extra => { :duplicates => duplicates })
end
end
This will let the user know there are duplicates, but along with adding the error message I would also like to store the duplicates so they can be displayed to the user. How would I store the list of duplicate posts retrieved during validation such that it is associated with the record's errors for the body field, and available to my view?
A simpler example might be length validation: If a field exceeds its maximum length, how can I store the maximum length along with an error message without simply interpolating it into the message as Rails currently does?
I have not had to do this before, but my first thought is to create a new method on the object called duplicates.
attr_accessor :duplicates
Then in your custom validate method, you can set the duplicates on the object making them available to the view when you render the errors. Notice your current code doesn't change much:
validate do |post|
duplicates = Post.find_all_by_body(body)
errors.add_to_base("Post is a duplicate!") if duplicates.size > 0
end
You would then have to intercept that error in the view manually so that you can print out all the duplicates if the "Post is a duplicate!" error is encountered.
You can pass options to the error but they are only used as substitution in i18n templates. To make a long story short, no you can't store meta-data about your error in the errors hash. If you need such a functionality you'll need to look into the ActiveModel::Errors module in Rails core.
Update:
Another solution could be that instead of pushing a string into error hash, you stuff an instance of your own class, a class which quacks like a string but would be decorated with extra methods and state and such like.