Articles > Odoo tricks and tools

Odoo tricks and tools

Written by
Holden Rehg
Posted on
March 3, 2019

Intro

There’s a set of built-in tools associated with the Odoo framework that many developers / third-party modules often overlook (found in the Odoo core under odoo/tools).

I’m going to outline and provide examples for some of the most common functions and variables.

Preface

All of the commands run below, I’m running in an Odoo shell instance. It makes it easy to test out all of the different tools available to you. If you aren’t sure how to run an Odoo shell, then see my article on it called Run An Odoo REPL.

The tools

config

Some helpers related to the odoo.conf configuration file or the configuration parameters passed into the odoo.py executable at start.

from odoo.tools import config


config.get("db_name")
# "odoo"

config.filestore(config.get("db_name"))
# "/var/lib/odoo/filestore/odoo"

config.session_dir
# "/var/lib/odoo/sessions"

config.options
# {
#     "addons_paths": "/opt/odoo/core/odoo/addons",
#     "auto_reload": False,
#     "db_host": "db",
# }
        

convert

Conversion helpers. These are primarily focused on conversion of data types such as xml and csv specific to Odoo, but one of the simpler function built into convert is str2bool that has many common use cases. Especially if working with APIs and integrations.

from odoo.tools import convert


convert.str2bool("0")  # False
convert.str2bool("false")  # False
convert.str2bool("False")  # False

convert.str2bool("1")  # True
convert.str2bool("true")  # True
convert.str2bool("True")  # True
        

date_utils — getters

There are a few built-in functions for getting ranges and references to different time periods. These are specific to certain periods like “month”, “quarter”, “fiscal year”, etc.

from odoo import fields
from odoo.tools import date_utils


sample_date = fields.Datetime.now()  # assume it's March 3rd, 2019

date_utils.get_month(sample_date)
# Returns the range for the first day of the month to the last
# (datetime.datetime(2019, 3, 1, 0, 0), datetime.datetime(2019, 3, 31, 0, 0))

date_utils.get_quarter(sample_date)
# Returns the range for the first day of the fiscal quarter to the last
# (datetime.datetime(2019, 1, 1, 0, 0), datetime.datetime(2019, 3, 31, 0, 0))

date_utils.get_quarter_number(sample_date)
# Returns the number of the current quarter (1, 2, 3, or 4) based on the date
# 1 in this case

date_utils.get_fiscal_year(sample_date)
# Returns the range for the first day of the fiscal year to the last
# (datetime.datetime(2019, 1, 1, 0, 0), datetime.datetime(2019, 12, 31, 0, 0))
        

date_utils — range helpers

Behind the scenes of the getter functions above, you also have generic range helpers.

from dateutil.relativedelta import relativedelta
from odoo import fields
from odoo.tools import date_utils


sample_date = fields.Datetime.now()  # assume it's March 3rd, 2019

date_utils.start_of(sample_date, "hour")  # datetime.datetime(2019, 3, 3, 17, 0)
date_utils.start_of(sample_date, "day")  # datetime.datetime(2019, 3, 3, 0, 0)
date_utils.start_of(sample_date, "week")  # datetime.datetime(2019, 2, 25, 0, 0)
date_utils.start_of(sample_date, "month")  # datetime.datetime(2019, 3, 1, 0, 0)
date_utils.start_of(sample_date, "quarter")  # datetime.datetime(2019, 1, 1, 0, 0)
date_utils.start_of(sample_date, "year")  # datetime.datetime(2019, 1, 1, 0, 0)

date_utils.end_of(sample_date, "hour")  # datetime.datetime(2019, 3, 3, 17 59, 59, 999999)
date_utils.end_of(sample_date, "day")  # datetime.datetime(2019, 3, 3, 23, 59, 59, 999999)
date_utils.end_of(sample_date, "week")  # datetime.datetime(2019, 3, 3, 23, 59, 59, 999999)
date_utils.end_of(sample_date, "month")  # datetime.datetime(2019, 3, 31, 23, 59, 59, 999999)
date_utils.end_of(sample_date, "quarter")  # datetime.datetime(2019, 3, 31, 23, 59, 59, 999999)
date_utils.end_of(sample_date, "year")  # datetime.datetime(2019, 12, 31, 23, 59, 59, 999999)

for date in date_utils.date_range(
    start=sample_date,
    end=date_utils.add(sample_date, days=15),
    step=relativedelta(days=1),
):
    print(date)

# 2019-03-03 17:29:03
# 2019-03-04 17:29:03
# 2019-03-05 17:29:03
# 2019-03-06 17:29:03
# 2019-03-07 17:29:03
# 2019-03-08 17:29:03
# 2019-03-09 17:29:03
# 2019-03-10 17:29:03
# 2019-03-11 17:29:03
# 2019-03-12 17:29:03
# 2019-03-13 17:29:03
# 2019-03-14 17:29:03
# 2019-03-15 17:29:03
# 2019-03-16 17:29:03
# 2019-03-17 17:29:03
# 2019-03-18 17:29:03
        

date_utils — calculations

Also in the date_utils, there are a couple of abstractions to make simple date calculations easy.

from odoo import fields
from odoo.tools import date_utils


sample_date = fields.Datetime.now()  # assume it's March 3rd, 2019

date_utils.add(sample_date, days=5)  # datetime.datetime(2019, 3, 8, 17, 29, 3)
date_utils.add(sample_date, weeks=2)  # datetime.datetime(2019, 3, 17, 17, 29, 3)
date_utils.add(sample_date, months=1)  # datetime.datetime(2019, 4, 3, 17, 29, 3)
date_utils.add(sample_date, years=1)  # datetime.datetime(2019, 3, 3, 17, 29, 3)
date_utils.add(sample_date, days=2, months=6, years=1)  # datetime.datetime(2019, 9, 5, 17, 29, 3)

date_utils.subtract(sample_date, days=5)  # datetime.datetime(2019, 2, 26, 17, 29, 3)
date_utils.subtract(sample_date, weeks=2)  # datetime.datetime(2019, 2, 17, 17, 29, 3)
date_utils.subtract(sample_date, months=1)  # datetime.datetime(2019, 2, 3, 17, 29, 3)
date_utils.subtract(sample_date, years=1)  # datetime.datetime(2019, 3, 3, 17, 29, 3)
date_utils.subtract(sample_date, days=2, months=6, years=1)  # datetime.datetime(2019, 9, 1, 17, 29, 3)
        

float_utils

A set of helpers related to floating point numbers.

from odoo.tools import float_utils


float_utils.float_round(
    1.5424,
    precision_digits=2,
    rounding_method="DOWN",
)  # 1.54

float_utils.float_round(
    1.5424,
    precision_digits=3,
    rounding_method="UP",
)  # 1.543

float_utils.float_round(
    1.5424,
    precision_digits=3,
    rounding_method="HALF-UP",
)  # 1.542

float_utils.float_is_zero(0.04252, precision_digits=5)  # False
float_utils.float_is_zero(0.04252, precision_digits=1)  # True

float_utils.float_compare(0.042555, 0.04256, precision_digits=5)  # 0 => equal
float_utils.float_compare(0.042555, 0.04256, precision_digits=6)  # -1 => value1 is lower than value2

        

image

Set of tools for manipulating images.

from odoo.tools import image


image.image_resize_image(img, size=(500, 500))
image.image_resize_and_sharpen(img, size=(500, 500))

with open("/tmp/my_image.png", "w") as f:
    image.image_save_for_web(img, f)
        

mail

Different helpers written to help create emails, but generally help with parsing and generating html.

from odoo.tools import mail


mail.html_keep_url("http://google.com")
# '<a href="https://google.com" rel="noreferrer" target="_blank">https://google.com</a>'

html = '''
<html>
  <head>
    <script src="/my/script.js"/>
  </head>
  <body>
    <p>
      <span>A cool html email with <a href="https://google.com">a link to goolge.</a></span>
    </p>
  </body>
</html>
'''

mail.html.sanitize(html)
# '

<p>
<span>A cool html email with <a href="https://google.com">a link to google.</a></span>'

mail.html2plaintext(html)
# A cool html email with a link to google.com. [1]
# [1] https://google.com

mail.plaintext2html(mail.html2plaintext(html))
# <p><br/>A cool html email with a link to google.com.</p>
        

misc

Lots of little miscellaneous helpers that come in handy.

from odoo.tools import misc


misc.DEFAULT_SERVER_DATE_FORMAT  # "%Y-%m-%d"
misc.DEFAULT_SERVER_TIME_FORMAT  # "%H:%M:%S"
misc.DEFAULT_SERVER_DATETIME_FORMAT  # "%Y-%m-%d %H:%M:%S"

misc.flatten([[["a", "b"], "c"], "d", ["e", [], "f"]])
# ["a", "b", "c", "d", "e", "f"]

for i, el in misc.reverse_enumerate(["a", "b", "c"]):
    print(i, el)

# 2 c
# 1 b
# 0 a

misc.topological_sort(
    {
        "a": ["b", "c"],
        "b": ["c"].
        "d": ["a"],
        "r": ["y"],
        "y": ["z"],
    }
)
# ["y", "b", "a", "r", "d"]

misc.scan_languages()
# [
#     ("sq_AL", "Albanian / Shqip")],
#     ...
#     ("zh_HK", "Chinese (HK)")],
#     ...
# ]

misc.human_size(1024, * 10)  # "10.00 Kb"
misc.human_size(1024 * 10000)  # "9.77 Mb"
misc.human_size(1024 * 10000000)  # "9.54 Gb"

for split in misc.split_every(iterable=["a", "b", "c", "d"], n=2):
    print(split)

# ("a", "b")
# ("c", "d")

misc.groupby(
    [
        {"first_name": "Jon", "last_name": "Stewart"},
        {"first_name": "Martha", "last_name": "Stewart"},
        {"first_name": "Rod", "last_name": "Stewart"},
        {"first_name": "Jow", "last_name": "Jackson"},
    ],
    key=lambda el: el["last_name"]
)
# dict_items(
#     [
#         ("Jackson", [
#             {"first_name": "Joe", "last_name": "Jackson"},
#         ],
#         ("Stewart": [
#             {"first_name": "Jon", "last_name": "Stewart"},
#             {"first_name": "Martha", "last_name": "Stewart"},
#             {"first_name": "Rod", "last_name": "Stewart"},
#         ])
#     ]
# )

list(misc.unique(["a", "a", "b", "c"]))
# ["a", "b", "c"]
        

The others

Check out some of the other modules that we didn’t cover. All of these are kept in the core repository under odoo/tools . There are tools for interaction with the local filesystem via osutil.py , pdf helpers via pdf.py , profiling and debugging tools under profiler.py or debugger.py , and some data structures built from scratch under graph.py and lru.py .

Thanks For Reading

I appreciate you taking the time to read any of my articles. I hope it has helped you out in some way. If you're looking for more ramblings, take a look at theentire catalog of articles I've written. Give me a follow on Twitter or Github to see what else I've got going on. Feel free to reach out if you want to talk!

python
odoo
erp
web development
Share:

Holden Rehg, Author

Posted March 3, 2019