Initial commit: Odoo 18.0-20251222 extra-addons
This commit is contained in:
151
database_size/README.rst
Executable file
151
database_size/README.rst
Executable file
@@ -0,0 +1,151 @@
|
||||
.. image:: https://odoo-community.org/readme-banner-image
|
||||
:target: https://odoo-community.org/get-involved?utm_source=readme
|
||||
:alt: Odoo Community Association
|
||||
|
||||
=============
|
||||
Database Size
|
||||
=============
|
||||
|
||||
..
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:57fdd3cd5e43a1434f9fe453728520f4dd445d90b738db359d34f40fa7d90328
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Beta
|
||||
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github
|
||||
:target: https://github.com/OCA/server-tools/tree/18.0/database_size
|
||||
:alt: OCA/server-tools
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-database_size
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
|
||||
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=18.0
|
||||
:alt: Try me on Runboat
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
Monitor the size of your Odoo instance.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
To configure this module, you can review the scheduled action called
|
||||
'Take model size measurements' and check the time at which you want it
|
||||
to run. It should only run once a day. If it runs more often, it just
|
||||
updates the existing set of sizes for the day.
|
||||
|
||||
You may also review the Database Size settings in Odoo's general
|
||||
settings and enable 'Purge Older Model Size Measurements'. This task
|
||||
will by default delete most daily data older than a year except for the
|
||||
data captured on the first day of each month. These retention periods
|
||||
can be configured here as well.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
You can use this module to keep an eye on the development of the size of
|
||||
your Odoo instance over time. Every day, a snapshot will be taken with
|
||||
the full size of the database and the attachments. You can query these
|
||||
daily snapshots, and you can compare the current size with a size at any
|
||||
date of the past for which there is data.
|
||||
|
||||
Enable debug mode, then go to menu Settings -> Technical -> Database
|
||||
Size.
|
||||
|
||||
|image1|
|
||||
|
||||
The data that is gathered and that is displayed are:
|
||||
|
||||
- Model Name - The name of the model to which the data is related
|
||||
- Estimated Rows - The number of estimated rows according to the
|
||||
Postgresql query planner. For performance reasons, taking the data
|
||||
from the planner is preferred over doing an actual count, although the
|
||||
results may be imprecise.
|
||||
- Bare Table Size - The disk usage of the model table without indexes
|
||||
etc.
|
||||
- Index Size - The disk usage of the indexes in the model table.
|
||||
- Many2many Tables Size - The disk usage of related many2many tables,
|
||||
including their indexes. To prevent double counts, many2many tables
|
||||
are only correlated with one of their tables (the largest of the two).
|
||||
- Attachment Size - The disk usage of the attachments linked to the
|
||||
model records. Because Odoo will deduplicate attachments by content,
|
||||
attachments with the same content may be counted double in the
|
||||
attachment size of other models, but will not be counted double when
|
||||
linked to records of the same model more than once.
|
||||
- Total Table Size - Bare Table Size + Index Size
|
||||
- Total Database Size - Total Table Size + Many2many Tables Size
|
||||
- Total Model Size - Total Database Size + Attachment Size
|
||||
|
||||
If you click on individual records, you can inspect the sizes of each
|
||||
index and many2many table.
|
||||
|
||||
All sizes are in megabytes.
|
||||
|
||||
In the 'Compare Size per Model' report view, you can find these data
|
||||
twice: once for the selected measurement date (default: today), and once
|
||||
for the selected comparison date (default: one month ago).
|
||||
|
||||
|image2|
|
||||
|
||||
If you want to compare arbitrary dates, you can start typing the date in
|
||||
the search box. Be sure to enter the dates in the right format for your
|
||||
localization.
|
||||
|
||||
|image3|
|
||||
|
||||
.. |image1| image:: https://raw.githubusercontent.com/OCA/server-tools/18.0/database_size/static/images/model_size.png
|
||||
.. |image2| image:: https://raw.githubusercontent.com/OCA/server-tools/18.0/database_size/static/images/compare_model_size.png
|
||||
.. |image3| image:: https://raw.githubusercontent.com/OCA/server-tools/18.0/database_size/static/images/select_date.png
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/OCA/server-tools/issues/new?body=module:%20database_size%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
* Opener B.V.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
- Stefan Rijnhart <stefan@opener.amsterdam>
|
||||
|
||||
Maintainers
|
||||
-----------
|
||||
|
||||
This module is maintained by the OCA.
|
||||
|
||||
.. image:: https://odoo-community.org/logo.png
|
||||
:alt: Odoo Community Association
|
||||
:target: https://odoo-community.org
|
||||
|
||||
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
This module is part of the `OCA/server-tools <https://github.com/OCA/server-tools/tree/18.0/database_size>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
2
database_size/__init__.py
Executable file
2
database_size/__init__.py
Executable file
@@ -0,0 +1,2 @@
|
||||
from . import models
|
||||
from . import report
|
||||
24
database_size/__manifest__.py
Executable file
24
database_size/__manifest__.py
Executable file
@@ -0,0 +1,24 @@
|
||||
# Copyright 2025 Opener B.V. <https://opener.amsterdam>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
{
|
||||
"name": "Database Size",
|
||||
"version": "18.0.1.0.2",
|
||||
"author": "Opener B.V.,Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/OCA/server-tools",
|
||||
"depends": ["base_setup"],
|
||||
"license": "AGPL-3",
|
||||
"category": "Tools",
|
||||
"data": [
|
||||
"data/ir_cron_data.xml",
|
||||
"security/ir.model.access.csv",
|
||||
"views/ir_model_size_views.xml",
|
||||
"views/res_config_settings_views.xml",
|
||||
"report/ir_model_size_report_views.xml",
|
||||
],
|
||||
"assets": {
|
||||
"web.assets_backend": [
|
||||
"database_size/static/src/scss/list_view_wrap_header.scss",
|
||||
]
|
||||
},
|
||||
"installable": True,
|
||||
}
|
||||
11
database_size/data/ir_cron_data.xml
Executable file
11
database_size/data/ir_cron_data.xml
Executable file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo noupdate="1">
|
||||
<record id="ir_cron_ir_model_size_measure" model="ir.cron">
|
||||
<field name="code">model._measure()</field>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="model_id" ref="model_ir_model_size" />
|
||||
<field name="name">Take model size measurements</field>
|
||||
<field name="state">code</field>
|
||||
</record>
|
||||
</odoo>
|
||||
397
database_size/i18n/database_size.pot
Executable file
397
database_size/i18n/database_size.pot
Executable file
@@ -0,0 +1,397 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * database_size
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 18.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.res_config_settings_view_form
|
||||
msgid "<span> days</span>"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__attachment_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__attachment_size
|
||||
msgid "Attachment Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__attachment_size
|
||||
msgid ""
|
||||
"Attachment Size in MB. Includes overlap of files that are also attached to "
|
||||
"other models."
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__table_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__table_size
|
||||
msgid "Bare Table Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__table_size
|
||||
msgid "Bare Table Size in MB."
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__diff_total_database_size
|
||||
msgid "Change in Total Database Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__diff_total_model_size
|
||||
msgid "Change in Total Model Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.actions.act_window,name:database_size.ir_model_size_report_action
|
||||
msgid "Compare Database Size per Model"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.ui.menu,name:database_size.ir_model_size_report_menu
|
||||
msgid "Compare Size per Model"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model,name:database_size.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__create_uid
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__create_uid
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__create_uid
|
||||
msgid "Created by"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__create_date
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__create_date
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__create_date
|
||||
msgid "Created on"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.ui.menu,name:database_size.database_size_menu
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.res_config_settings_view_form
|
||||
msgid "Database Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.actions.act_window,name:database_size.ir_model_size_action
|
||||
msgid "Database Size per Model"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__measurement_date
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__measurement_date
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_view_search
|
||||
msgid "Date of Measurement"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_report_view_tree
|
||||
msgid "Details"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model,name:database_size.model_ir_model_index_size
|
||||
msgid "Disk space usage of a single index"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model,name:database_size.model_ir_model_relation_size
|
||||
msgid "Disk space usage of a single many2many table"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model,name:database_size.model_ir_model_size
|
||||
msgid "Disk space usage per model"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__display_name
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__display_name
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__display_name
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__display_name
|
||||
msgid "Display Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__tuples
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__tuples
|
||||
msgid "Estimated Rows"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__measurement_date
|
||||
msgid "For the exact time, check the record's write_date."
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_view_search
|
||||
msgid "Group By"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_attachment_size
|
||||
msgid "Historical Attachment Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_table_size
|
||||
msgid "Historical Bare Table Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_measurement_date
|
||||
msgid "Historical Date of Measurement"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model,name:database_size.model_ir_model_size_report
|
||||
msgid "Historical Disk space usage per model"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_tuples
|
||||
msgid "Historical Estimated Rows"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_indexes_size
|
||||
msgid "Historical Index Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_relations_size
|
||||
msgid "Historical Many2many Tables Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_total_database_size
|
||||
msgid "Historical Total Database Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_total_model_size
|
||||
msgid "Historical Total Model Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_total_table_size
|
||||
msgid "Historical Total Table Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__id
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__id
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__id
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__id
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__indexes_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__indexes_size
|
||||
msgid "Index Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__ir_model_index_size_ids
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_view_form
|
||||
msgid "Indexes"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__ir_model_size_id
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__ir_model_size_id
|
||||
msgid "Ir Model Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_res_config_settings__database_size_retention_daily
|
||||
msgid "Keep Daily Measurements for"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_res_config_settings__database_size_retention_monthly
|
||||
msgid "Keep Monthly Measurements for"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__write_uid
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__write_uid
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__write_date
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__write_date
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__relations_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__relations_size
|
||||
msgid "Many2many Tables Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_view_form
|
||||
msgid "Many2many tables"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__model
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__model
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_view_search
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__model_name
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__model_name
|
||||
msgid "Model Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__name
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__name
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_report_view_search
|
||||
msgid "Notable Change"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_report_view_search
|
||||
msgid "One Month Ago"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_report_view_search
|
||||
msgid "One Year Ago"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_res_config_settings__database_size_purge
|
||||
msgid "Purge Older Model Size Measurements"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__ir_model_relation_size_ids
|
||||
msgid "Relations"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__tuples
|
||||
msgid "Rows in use, including dead tuples"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.res_config_settings_view_form
|
||||
msgid "Set to 0 to keep monthly measurements forever."
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__size
|
||||
msgid "Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.ui.menu,name:database_size.ir_model_size_menu
|
||||
msgid "Size per Model"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__smart_search
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__smart_search
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__smart_search
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__smart_search
|
||||
#: model:ir.model.fields,field_description:database_size.field_res_config_settings__smart_search
|
||||
msgid "Smart Search"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.actions.server,name:database_size.ir_cron_ir_model_size_measure_ir_actions_server
|
||||
msgid "Take model size measurements"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_res_config_settings__database_size_retention_monthly
|
||||
msgid ""
|
||||
"The period of time (in days) during which database size measurmeents are "
|
||||
"kept of the first day of each month. If set to 0, measurements will be kept "
|
||||
"forever."
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_res_config_settings__database_size_retention_daily
|
||||
msgid ""
|
||||
"The period of time (in days) during which the daily database size "
|
||||
"measurements are kept. If set to 0, measurements will be kept forever."
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.constraint,message:database_size.constraint_ir_model_size_uniq_model_measurement_date
|
||||
msgid "There is already a measurement for this model on the given date"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__total_database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__total_database_size
|
||||
msgid "Total Database Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__total_model_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__total_model_size
|
||||
msgid "Total Model Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__total_database_size
|
||||
msgid "Total Model Size in MB. This includes many2many tables"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__indexes_size
|
||||
msgid "Total Size of Indexes in MB"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__relations_size
|
||||
msgid "Total Size of many2many relations in MB"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__total_table_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__total_table_size
|
||||
msgid "Total Table Size"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__total_table_size
|
||||
msgid "Total Table Size in MB. This includes indexes and toast tables"
|
||||
msgstr ""
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__total_model_size
|
||||
msgid "Total model size in MB. This includes attachments."
|
||||
msgstr ""
|
||||
408
database_size/i18n/it.po
Executable file
408
database_size/i18n/it.po
Executable file
@@ -0,0 +1,408 @@
|
||||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * database_size
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 18.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-05-29 11:25+0000\n"
|
||||
"Last-Translator: Sergio Zanchetta <primes2h@gmail.com>\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: it\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.10.4\n"
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.res_config_settings_view_form
|
||||
msgid "<span> days</span>"
|
||||
msgstr "<span> giorni</span>"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__attachment_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__attachment_size
|
||||
msgid "Attachment Size"
|
||||
msgstr "Dimensione allegato"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__attachment_size
|
||||
msgid ""
|
||||
"Attachment Size in MB. Includes overlap of files that are also attached to "
|
||||
"other models."
|
||||
msgstr ""
|
||||
"Dimensione allegato in MB. Include la sovrapposizione di file che sono "
|
||||
"allegati anche ad altri modelli."
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__table_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__table_size
|
||||
msgid "Bare Table Size"
|
||||
msgstr "Dimensione tabella vuota"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__table_size
|
||||
msgid "Bare Table Size in MB."
|
||||
msgstr "Dimensione tabella vuota in MB."
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__diff_total_database_size
|
||||
msgid "Change in Total Database Size"
|
||||
msgstr "Modifica della dimensione totale del database"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__diff_total_model_size
|
||||
msgid "Change in Total Model Size"
|
||||
msgstr "Modifica nella dimensione totale del modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.actions.act_window,name:database_size.ir_model_size_report_action
|
||||
msgid "Compare Database Size per Model"
|
||||
msgstr "Compara dimensione database per modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.ui.menu,name:database_size.ir_model_size_report_menu
|
||||
msgid "Compare Size per Model"
|
||||
msgstr "Compara dimensione per modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model,name:database_size.model_res_config_settings
|
||||
msgid "Config Settings"
|
||||
msgstr "Impostazioni configurazione"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__create_uid
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__create_uid
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__create_uid
|
||||
msgid "Created by"
|
||||
msgstr "Creato da"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__create_date
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__create_date
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__create_date
|
||||
msgid "Created on"
|
||||
msgstr "Creato il"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.ui.menu,name:database_size.database_size_menu
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.res_config_settings_view_form
|
||||
msgid "Database Size"
|
||||
msgstr "Dimensione database"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.actions.act_window,name:database_size.ir_model_size_action
|
||||
msgid "Database Size per Model"
|
||||
msgstr "Dimensione database per modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__measurement_date
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__measurement_date
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_view_search
|
||||
msgid "Date of Measurement"
|
||||
msgstr "Data della misura"
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_report_view_tree
|
||||
msgid "Details"
|
||||
msgstr "Dettagli"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model,name:database_size.model_ir_model_index_size
|
||||
msgid "Disk space usage of a single index"
|
||||
msgstr "Utilizzo spazio su disco per singolo indice"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model,name:database_size.model_ir_model_relation_size
|
||||
msgid "Disk space usage of a single many2many table"
|
||||
msgstr "Utilizzo spazio su disco per una singola tabella many2many"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model,name:database_size.model_ir_model_size
|
||||
msgid "Disk space usage per model"
|
||||
msgstr "Utilizzo spazio su disco per modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__display_name
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__display_name
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__display_name
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__display_name
|
||||
msgid "Display Name"
|
||||
msgstr "Nome visualizzato"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__tuples
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__tuples
|
||||
msgid "Estimated Rows"
|
||||
msgstr "Righe stimate"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__measurement_date
|
||||
msgid "For the exact time, check the record's write_date."
|
||||
msgstr "Per l'orario esatto, controllare la write_date del record."
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_view_search
|
||||
msgid "Group By"
|
||||
msgstr "Raggruppa per"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_attachment_size
|
||||
msgid "Historical Attachment Size"
|
||||
msgstr "Cronologia dimensione allegato"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_table_size
|
||||
msgid "Historical Bare Table Size"
|
||||
msgstr "Cronologia dimensione tabella vuota"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_measurement_date
|
||||
msgid "Historical Date of Measurement"
|
||||
msgstr "Cronologia data della misura"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model,name:database_size.model_ir_model_size_report
|
||||
msgid "Historical Disk space usage per model"
|
||||
msgstr "Cronologia utilizzo disco per modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_tuples
|
||||
msgid "Historical Estimated Rows"
|
||||
msgstr "Cronologica righe stimate"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_indexes_size
|
||||
msgid "Historical Index Size"
|
||||
msgstr "Cronologia dimensione indice"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_relations_size
|
||||
msgid "Historical Many2many Tables Size"
|
||||
msgstr "Cronologia dimensione tabelle many2many"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_total_database_size
|
||||
msgid "Historical Total Database Size"
|
||||
msgstr "Cronologia dimensione totale database"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_total_model_size
|
||||
msgid "Historical Total Model Size"
|
||||
msgstr "Cronologia dimensione totale modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__historical_total_table_size
|
||||
msgid "Historical Total Table Size"
|
||||
msgstr "Cronologia dimensione totale tabella"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__id
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__id
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__id
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__id
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__indexes_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__indexes_size
|
||||
msgid "Index Size"
|
||||
msgstr "Dimensione indice"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__ir_model_index_size_ids
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_view_form
|
||||
msgid "Indexes"
|
||||
msgstr "Indici"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__ir_model_size_id
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__ir_model_size_id
|
||||
msgid "Ir Model Size"
|
||||
msgstr "Dimensione modello ir"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_res_config_settings__database_size_retention_daily
|
||||
msgid "Keep Daily Measurements for"
|
||||
msgstr "Mantieni misure giornaliere per"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_res_config_settings__database_size_retention_monthly
|
||||
msgid "Keep Monthly Measurements for"
|
||||
msgstr "Mantieni misure mensili per"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__write_uid
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__write_uid
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__write_uid
|
||||
msgid "Last Updated by"
|
||||
msgstr "Ultimo aggiornamento di"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__write_date
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__write_date
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__write_date
|
||||
msgid "Last Updated on"
|
||||
msgstr "Ultimo aggiornamento il"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__relations_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__relations_size
|
||||
msgid "Many2many Tables Size"
|
||||
msgstr "Dimensione tabelle many2many"
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_view_form
|
||||
msgid "Many2many tables"
|
||||
msgstr "Tabelle many2many"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__model
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__model
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_view_search
|
||||
msgid "Model"
|
||||
msgstr "Modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__model_name
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__model_name
|
||||
msgid "Model Name"
|
||||
msgstr "Nome modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__name
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__name
|
||||
msgid "Name"
|
||||
msgstr "Nome"
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_report_view_search
|
||||
msgid "Notable Change"
|
||||
msgstr "Modifica rilevante"
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_report_view_search
|
||||
msgid "One Month Ago"
|
||||
msgstr "Un mese fa"
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.ir_model_size_report_view_search
|
||||
msgid "One Year Ago"
|
||||
msgstr "Un anno fa"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_res_config_settings__database_size_purge
|
||||
msgid "Purge Older Model Size Measurements"
|
||||
msgstr "Ripulisci vecchie misure dimensione modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__ir_model_relation_size_ids
|
||||
msgid "Relations"
|
||||
msgstr "Relazioni"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__tuples
|
||||
msgid "Rows in use, including dead tuples"
|
||||
msgstr "Righe in uso incluse tuple inutilizzate"
|
||||
|
||||
#. module: database_size
|
||||
#: model_terms:ir.ui.view,arch_db:database_size.res_config_settings_view_form
|
||||
msgid "Set to 0 to keep monthly measurements forever."
|
||||
msgstr "Impostare a 0 per mantenere le misure mensili per sempre."
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__size
|
||||
msgid "Size"
|
||||
msgstr "Dimensione"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.ui.menu,name:database_size.ir_model_size_menu
|
||||
msgid "Size per Model"
|
||||
msgstr "Dimensione per modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_index_size__smart_search
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_relation_size__smart_search
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__smart_search
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__smart_search
|
||||
#: model:ir.model.fields,field_description:database_size.field_res_config_settings__smart_search
|
||||
msgid "Smart Search"
|
||||
msgstr "Ricerca intelligente"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.actions.server,name:database_size.ir_cron_ir_model_size_measure_ir_actions_server
|
||||
msgid "Take model size measurements"
|
||||
msgstr "Rileva le misure della dimensione del modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_res_config_settings__database_size_retention_monthly
|
||||
msgid ""
|
||||
"The period of time (in days) during which database size measurmeents are "
|
||||
"kept of the first day of each month. If set to 0, measurements will be kept "
|
||||
"forever."
|
||||
msgstr ""
|
||||
"Periodo di tempo (in giorni) durante il quale vengono conservate le "
|
||||
"misurazioni delle dimensioni del database, a partire dal primo giorno di "
|
||||
"ogni mese. Se impostato su 0, le misurazioni verranno conservate per sempre."
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_res_config_settings__database_size_retention_daily
|
||||
msgid ""
|
||||
"The period of time (in days) during which the daily database size "
|
||||
"measurements are kept. If set to 0, measurements will be kept forever."
|
||||
msgstr ""
|
||||
"Periodo di tempo (in giorni) durante il quale vengono conservate le "
|
||||
"misurazioni giornaliere delle dimensioni del database. Se impostato su 0, le "
|
||||
"misurazioni verranno conservate per sempre."
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.constraint,message:database_size.constraint_ir_model_size_uniq_model_measurement_date
|
||||
msgid "There is already a measurement for this model on the given date"
|
||||
msgstr "Esiste già una misurazione per questo modello alla data indicata"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__total_database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__total_database_size
|
||||
msgid "Total Database Size"
|
||||
msgstr "Dimensione totale database"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__total_model_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__total_model_size
|
||||
msgid "Total Model Size"
|
||||
msgstr "Dimensione totale modello"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__total_database_size
|
||||
msgid "Total Model Size in MB. This includes many2many tables"
|
||||
msgstr "Dimensione totale modello in MB. Include le tabelle many2many"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__indexes_size
|
||||
msgid "Total Size of Indexes in MB"
|
||||
msgstr "Dimensione totale indici in MB"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__relations_size
|
||||
msgid "Total Size of many2many relations in MB"
|
||||
msgstr "Dimensione totale relazioni many2many in MB"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size__total_table_size
|
||||
#: model:ir.model.fields,field_description:database_size.field_ir_model_size_report__total_table_size
|
||||
msgid "Total Table Size"
|
||||
msgstr "Dimensione totale tabella"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__total_table_size
|
||||
msgid "Total Table Size in MB. This includes indexes and toast tables"
|
||||
msgstr "Dimensione totale tabella in MB. include gli indici e le tabelle toast"
|
||||
|
||||
#. module: database_size
|
||||
#: model:ir.model.fields,help:database_size.field_ir_model_size__total_model_size
|
||||
msgid "Total model size in MB. This includes attachments."
|
||||
msgstr "Dimensione totale modello in MB inclusi gli allegati."
|
||||
4
database_size/models/__init__.py
Executable file
4
database_size/models/__init__.py
Executable file
@@ -0,0 +1,4 @@
|
||||
from . import ir_model_size
|
||||
from . import ir_model_index_size
|
||||
from . import ir_model_relation_size
|
||||
from . import res_config_settings
|
||||
18
database_size/models/ir_model_index_size.py
Executable file
18
database_size/models/ir_model_index_size.py
Executable file
@@ -0,0 +1,18 @@
|
||||
# Copyright 2025 Opener B.V. <https://opener.amsterdam>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class IrModelIndexSize(models.Model):
|
||||
_name = "ir.model.index.size"
|
||||
_description = "Disk space usage of a single index"
|
||||
_order = "ir_model_size_id desc, size desc"
|
||||
|
||||
name = fields.Char(required=True)
|
||||
ir_model_size_id = fields.Many2one(
|
||||
comodel_name="ir.model.size",
|
||||
index=True,
|
||||
ondelete="cascade",
|
||||
required=True,
|
||||
)
|
||||
size = fields.Integer()
|
||||
18
database_size/models/ir_model_relation_size.py
Executable file
18
database_size/models/ir_model_relation_size.py
Executable file
@@ -0,0 +1,18 @@
|
||||
# Copyright 2025 Opener B.V. <https://opener.amsterdam>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class IrModelRelationSize(models.Model):
|
||||
_name = "ir.model.relation.size"
|
||||
_description = "Disk space usage of a single many2many table"
|
||||
_order = "ir_model_size_id desc, size desc"
|
||||
|
||||
name = fields.Char(required=True)
|
||||
ir_model_size_id = fields.Many2one(
|
||||
comodel_name="ir.model.size",
|
||||
index=True,
|
||||
ondelete="cascade",
|
||||
required=True,
|
||||
)
|
||||
size = fields.Integer()
|
||||
316
database_size/models/ir_model_size.py
Executable file
316
database_size/models/ir_model_size.py
Executable file
@@ -0,0 +1,316 @@
|
||||
# Copyright 2025 Opener B.V. <https://opener.amsterdam>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IrModelSize(models.Model):
|
||||
_name = "ir.model.size"
|
||||
_description = "Disk space usage per model"
|
||||
_order = "measurement_date desc, total_model_size desc"
|
||||
_rec_name = "model"
|
||||
_sql_constraints = [
|
||||
(
|
||||
"uniq_model_measurement_date",
|
||||
"unique(model, measurement_date)",
|
||||
"There is already a measurement for this model on the given date",
|
||||
),
|
||||
]
|
||||
model = fields.Char(index=True)
|
||||
model_name = fields.Char(
|
||||
compute="_compute_model_name",
|
||||
store=True,
|
||||
)
|
||||
measurement_date = fields.Date(
|
||||
"Date of Measurement",
|
||||
help="For the exact time, check the record's write_date.",
|
||||
required=True,
|
||||
)
|
||||
total_model_size = fields.Integer(
|
||||
compute="_compute_total_sizes",
|
||||
help="Total model size in MB. This includes attachments.",
|
||||
store=True,
|
||||
)
|
||||
total_database_size = fields.Integer(
|
||||
compute="_compute_total_sizes",
|
||||
help="Total Model Size in MB. This includes many2many tables",
|
||||
store=True,
|
||||
)
|
||||
total_table_size = fields.Integer(
|
||||
help="Total Table Size in MB. This includes indexes and toast tables",
|
||||
)
|
||||
table_size = fields.Integer(
|
||||
string="Bare Table Size",
|
||||
help="Bare Table Size in MB.",
|
||||
)
|
||||
ir_model_index_size_ids = fields.One2many(
|
||||
comodel_name="ir.model.index.size",
|
||||
inverse_name="ir_model_size_id",
|
||||
string="Indexes",
|
||||
)
|
||||
ir_model_relation_size_ids = fields.One2many(
|
||||
comodel_name="ir.model.relation.size",
|
||||
inverse_name="ir_model_size_id",
|
||||
string="Relations",
|
||||
)
|
||||
indexes_size = fields.Integer(
|
||||
compute="_compute_indexes_size",
|
||||
help="Total Size of Indexes in MB",
|
||||
store=True,
|
||||
string="Index Size",
|
||||
)
|
||||
relations_size = fields.Integer(
|
||||
compute="_compute_relations_size",
|
||||
help="Total Size of many2many relations in MB",
|
||||
store=True,
|
||||
string="Many2many Tables Size",
|
||||
)
|
||||
tuples = fields.Integer(
|
||||
string="Estimated Rows",
|
||||
help="Rows in use, including dead tuples",
|
||||
)
|
||||
attachment_size = fields.Integer(
|
||||
help=(
|
||||
"Attachment Size in MB. Includes overlap of files that are also "
|
||||
"attached to other models."
|
||||
),
|
||||
)
|
||||
|
||||
@api.depends("model")
|
||||
def _compute_model_name(self):
|
||||
"""Assign the model's label"""
|
||||
model2name = {
|
||||
model.model: model.name for model in self.env["ir.model"].sudo().search([])
|
||||
}
|
||||
for size in self:
|
||||
size.model_name = model2name.get(size.model, "<removed>")
|
||||
|
||||
@api.model
|
||||
def read_group(
|
||||
self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True
|
||||
):
|
||||
"""Enforce that grouped results are ordered.
|
||||
|
||||
Odoo will happily use the grouping field for ordering unless groupby is a
|
||||
list, and as it happens the grouping is usually passed as a list, for
|
||||
example: ['measurement_date:day']
|
||||
"""
|
||||
if not orderby and groupby and isinstance(groupby, list | set):
|
||||
field = groupby[0].split(":")[0]
|
||||
orderby = f"{field} desc"
|
||||
return super().read_group(
|
||||
domain,
|
||||
fields,
|
||||
groupby,
|
||||
offset=offset,
|
||||
limit=limit,
|
||||
orderby=orderby,
|
||||
lazy=lazy,
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
"total_table_size",
|
||||
"relations_size",
|
||||
"attachment_size",
|
||||
)
|
||||
def _compute_total_sizes(self):
|
||||
for size in self:
|
||||
size.total_database_size = size.total_table_size + size.relations_size
|
||||
size.total_model_size = size.total_database_size + size.attachment_size
|
||||
|
||||
@api.depends("ir_model_index_size_ids", "ir_model_index_size_ids.size")
|
||||
def _compute_indexes_size(self):
|
||||
for size in self:
|
||||
size.indexes_size = sum(size.ir_model_index_size_ids.mapped("size"))
|
||||
|
||||
@api.depends("ir_model_relation_size_ids", "ir_model_relation_size_ids.size")
|
||||
def _compute_relations_size(self):
|
||||
for size in self:
|
||||
size.relations_size = sum(size.ir_model_relation_size_ids.mapped("size"))
|
||||
|
||||
@staticmethod
|
||||
def _normalize_size(size):
|
||||
"""Filter out -1s and compute as MB"""
|
||||
if not size:
|
||||
return 0
|
||||
return int(max(0, size) / (1024 * 1024))
|
||||
|
||||
@api.model
|
||||
def _measure(self):
|
||||
"""Create the entries for today's report"""
|
||||
today = fields.Date.context_today(self)
|
||||
# Remove any previous report for the same day
|
||||
self.search([("measurement_date", "=", today)]).unlink()
|
||||
table2model = {}
|
||||
for model in self.env.values():
|
||||
if not model._abstract and not model._transient:
|
||||
model_model = model._name
|
||||
table2model[model._table] = model_model
|
||||
model2vals = {
|
||||
model_model: {
|
||||
"model": model_model,
|
||||
"measurement_date": today,
|
||||
"ir_model_index_size_ids": [],
|
||||
"ir_model_relation_size_ids": [],
|
||||
}
|
||||
for model_model in table2model.values()
|
||||
}
|
||||
# Some many2many relation objects are linked explicitely to both models
|
||||
# involved. To prevent counting them double, we will link them to the
|
||||
# largest table. Gather all the related models first.
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
select name, array_agg(model)
|
||||
from ir_model_relation group by name;
|
||||
"""
|
||||
)
|
||||
relation2model = dict(self.env.cr.fetchall())
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
SELECT relname,
|
||||
reltuples,
|
||||
pg_total_relation_size (C.oid),
|
||||
pg_relation_size (C.oid)
|
||||
FROM pg_class C
|
||||
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
|
||||
WHERE nspname NOT IN (
|
||||
'information_schema',
|
||||
'pg_catalog',
|
||||
'pg_logical',
|
||||
'pg_toast'
|
||||
)
|
||||
AND C.relkind = 'r'
|
||||
"""
|
||||
)
|
||||
# Gather sizes of model tables and many2many tables
|
||||
rows = self.env.cr.fetchall()
|
||||
for table, tuples, total_table_size, table_size in rows:
|
||||
model = table2model.get(table)
|
||||
if model:
|
||||
model2vals[model].update(
|
||||
{
|
||||
"table_size": self._normalize_size(table_size),
|
||||
"total_table_size": self._normalize_size(total_table_size),
|
||||
"tuples": max(tuples, 0),
|
||||
}
|
||||
)
|
||||
# Second pass to throw in the relation tables with the largest relation
|
||||
for table, _tuples, total_table_size, _table_size in rows:
|
||||
if table in relation2model:
|
||||
models = relation2model[table]
|
||||
model = sorted(
|
||||
models,
|
||||
key=lambda model: model2vals.get(model, {"tuples": -99})["tuples"],
|
||||
reverse=True,
|
||||
)[0]
|
||||
vals = model2vals.get(model)
|
||||
if vals:
|
||||
vals["ir_model_relation_size_ids"].append(
|
||||
fields.Command.create(
|
||||
{
|
||||
"name": table,
|
||||
"size": self._normalize_size(total_table_size),
|
||||
}
|
||||
)
|
||||
)
|
||||
# Gather sizes of indexes
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
SELECT i.relname table_name,
|
||||
indexrelname index_name,
|
||||
pg_relation_size(indexrelid) index_size
|
||||
FROM pg_stat_all_indexes i
|
||||
JOIN pg_class c ON i.relid=c.oid
|
||||
WHERE schemaname NOT IN (
|
||||
'information_schema',
|
||||
'pg_catalog',
|
||||
'pg_toast',
|
||||
'pg_logical'
|
||||
);
|
||||
"""
|
||||
)
|
||||
for table, index, size in self.env.cr.fetchall():
|
||||
vals = model2vals.get(table2model.get(table))
|
||||
if vals:
|
||||
vals["ir_model_index_size_ids"].append(
|
||||
fields.Command.create(
|
||||
{
|
||||
"name": index,
|
||||
"size": self._normalize_size(size),
|
||||
}
|
||||
)
|
||||
)
|
||||
# Gather sizes of attachments. Deduplicate by checksum such that the
|
||||
# attachment is attributed to the first model it was linked to.
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
with unique_attachments as (
|
||||
select res_model,
|
||||
file_size,
|
||||
row_number() over (partition by checksum order by id) as rowno
|
||||
from ir_attachment
|
||||
)
|
||||
select res_model, sum(file_size)
|
||||
from unique_attachments
|
||||
where rowno = 1
|
||||
group by res_model;
|
||||
"""
|
||||
)
|
||||
for model, size in self.env.cr.fetchall():
|
||||
vals = model2vals.get(model)
|
||||
if vals:
|
||||
vals["attachment_size"] = self._normalize_size(size)
|
||||
vals_list = [val for val in model2vals.values() if "table_size" in val]
|
||||
self.create(vals_list)
|
||||
_logger.info("Created %s model database size records", len(vals_list))
|
||||
|
||||
@api.autovacuum
|
||||
def _purge(self):
|
||||
"""Remove older model size records, if enabled in the General Settings."""
|
||||
if (
|
||||
not self.env["ir.config_parameter"]
|
||||
.sudo()
|
||||
.get_param("database_size.purge_enable")
|
||||
):
|
||||
return
|
||||
retention_daily = int(
|
||||
self.env["ir.config_parameter"]
|
||||
.sudo()
|
||||
.get_param("database_size.retention_daily", 366)
|
||||
)
|
||||
retention_monthly = int(
|
||||
self.env["ir.config_parameter"]
|
||||
.sudo()
|
||||
.get_param("database_size.retention_monthly", 0)
|
||||
)
|
||||
if retention_daily:
|
||||
cutoff_date = fields.Date.today() - timedelta(days=retention_daily)
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
delete from ir_model_size
|
||||
where measurement_date < %(cutoff_date)s
|
||||
and extract(day from measurement_date) != 1;
|
||||
""",
|
||||
{"cutoff_date": cutoff_date},
|
||||
)
|
||||
_logger.info(
|
||||
f"Deleted {self.env.cr.rowcount} ir_model_size from before "
|
||||
f"{cutoff_date} from any other day than the first day of the month."
|
||||
)
|
||||
if retention_monthly and retention_monthly > retention_daily:
|
||||
cutoff_date = fields.Date.today() - timedelta(days=retention_monthly)
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
delete from ir_model_size
|
||||
where measurement_date < %(cutoff_date)s;
|
||||
""",
|
||||
{"cutoff_date": cutoff_date},
|
||||
)
|
||||
_logger.info(
|
||||
f"Deleted {self.env.cr.rowcount} ir_model_size from before "
|
||||
f"{cutoff_date}."
|
||||
)
|
||||
32
database_size/models/res_config_settings.py
Executable file
32
database_size/models/res_config_settings.py
Executable file
@@ -0,0 +1,32 @@
|
||||
# Copyright 2025 Opener B.V. <https://opener.amsterdam>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
database_size_purge = fields.Boolean(
|
||||
string="Purge Older Model Size Measurements",
|
||||
config_parameter="database_size.purge_enable",
|
||||
)
|
||||
database_size_retention_daily = fields.Integer(
|
||||
string="Keep Daily Measurements for",
|
||||
config_parameter="database_size.retention_daily",
|
||||
help=(
|
||||
"The period of time (in days) during which the daily database size "
|
||||
"measurements are kept. If set to 0, measurements will be kept "
|
||||
"forever."
|
||||
),
|
||||
default="366",
|
||||
)
|
||||
database_size_retention_monthly = fields.Integer(
|
||||
string="Keep Monthly Measurements for",
|
||||
config_parameter="database_size.retention_monthly",
|
||||
help=(
|
||||
"The period of time (in days) during which database size measurmeents "
|
||||
"are kept of the first day of each month. If set to 0, measurements "
|
||||
"will be kept forever."
|
||||
),
|
||||
default="0",
|
||||
)
|
||||
3
database_size/pyproject.toml
Executable file
3
database_size/pyproject.toml
Executable file
@@ -0,0 +1,3 @@
|
||||
[build-system]
|
||||
requires = ["whool"]
|
||||
build-backend = "whool.buildapi"
|
||||
9
database_size/readme/CONFIGURE.md
Executable file
9
database_size/readme/CONFIGURE.md
Executable file
@@ -0,0 +1,9 @@
|
||||
To configure this module, you can review the scheduled action called 'Take model
|
||||
size measurements' and check the time at which you want it to run. It should
|
||||
only run once a day. If it runs more often, it just updates the existing set of
|
||||
sizes for the day.
|
||||
|
||||
You may also review the Database Size settings in Odoo's general settings and
|
||||
enable 'Purge Older Model Size Measurements'. This task will by default delete
|
||||
most daily data older than a year except for the data captured on the first day
|
||||
of each month. These retention periods can be configured here as well.
|
||||
1
database_size/readme/CONTRIBUTORS.md
Executable file
1
database_size/readme/CONTRIBUTORS.md
Executable file
@@ -0,0 +1 @@
|
||||
- Stefan Rijnhart \<<stefan@opener.amsterdam>\>
|
||||
1
database_size/readme/DESCRIPTION.md
Executable file
1
database_size/readme/DESCRIPTION.md
Executable file
@@ -0,0 +1 @@
|
||||
Monitor the size of your Odoo instance.
|
||||
33
database_size/readme/USAGE.md
Executable file
33
database_size/readme/USAGE.md
Executable file
@@ -0,0 +1,33 @@
|
||||
You can use this module to keep an eye on the development of the size of your
|
||||
Odoo instance over time. Every day, a snapshot will be taken with the full size
|
||||
of the database and the attachments. You can query these daily snapshots, and
|
||||
you can compare the current size with a size at any date of the past for which
|
||||
there is data.
|
||||
|
||||
Enable debug mode, then go to menu Settings -> Technical -> Database Size.
|
||||
|
||||

|
||||
|
||||
The data that is gathered and that is displayed are:
|
||||
|
||||
* Model Name - The name of the model to which the data is related
|
||||
* Estimated Rows - The number of estimated rows according to the Postgresql query planner. For performance reasons, taking the data from the planner is preferred over doing an actual count, although the results may be imprecise.
|
||||
* Bare Table Size - The disk usage of the model table without indexes etc.
|
||||
* Index Size - The disk usage of the indexes in the model table.
|
||||
* Many2many Tables Size - The disk usage of related many2many tables, including their indexes. To prevent double counts, many2many tables are only correlated with one of their tables (the largest of the two).
|
||||
* Attachment Size - The disk usage of the attachments linked to the model records. Because Odoo will deduplicate attachments by content, attachments with the same content may be counted double in the attachment size of other models, but will not be counted double when linked to records of the same model more than once.
|
||||
* Total Table Size - Bare Table Size + Index Size
|
||||
* Total Database Size - Total Table Size + Many2many Tables Size
|
||||
* Total Model Size - Total Database Size + Attachment Size
|
||||
|
||||
If you click on individual records, you can inspect the sizes of each index and many2many table.
|
||||
|
||||
All sizes are in megabytes.
|
||||
|
||||
In the 'Compare Size per Model' report view, you can find these data twice: once for the selected measurement date (default: today), and once for the selected comparison date (default: one month ago).
|
||||
|
||||

|
||||
|
||||
If you want to compare arbitrary dates, you can start typing the date in the search box. Be sure to enter the dates in the right format for your localization.
|
||||
|
||||

|
||||
3
database_size/report/__init__.py
Executable file
3
database_size/report/__init__.py
Executable file
@@ -0,0 +1,3 @@
|
||||
# Copyright 2025 Opener B.V. <https://opener.amsterdam>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from . import ir_model_size_report
|
||||
232
database_size/report/ir_model_size_report.py
Executable file
232
database_size/report/ir_model_size_report.py
Executable file
@@ -0,0 +1,232 @@
|
||||
# Copyright 2025 Opener B.V. <https://opener.amsterdam>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class IrModelSizeReport(models.Model):
|
||||
_name = "ir.model.size.report"
|
||||
_description = "Historical Disk space usage per model"
|
||||
_auto = False
|
||||
_rec_name = "historical_measurement_date"
|
||||
_order = "historical_measurement_date desc, diff_total_model_size desc"
|
||||
|
||||
model = fields.Char()
|
||||
model_name = fields.Char()
|
||||
|
||||
measurement_date = fields.Date("Date of Measurement")
|
||||
historical_measurement_date = fields.Date("Historical Date of Measurement")
|
||||
|
||||
total_model_size = fields.Integer()
|
||||
historical_total_model_size = fields.Integer()
|
||||
diff_total_model_size = fields.Integer("Change in Total Model Size")
|
||||
|
||||
total_database_size = fields.Integer()
|
||||
historical_total_database_size = fields.Integer()
|
||||
diff_total_database_size = fields.Integer("Change in Total Database Size")
|
||||
|
||||
total_table_size = fields.Integer()
|
||||
historical_total_table_size = fields.Integer()
|
||||
|
||||
table_size = fields.Integer("Bare Table Size")
|
||||
historical_table_size = fields.Integer("Historical Bare Table Size")
|
||||
|
||||
indexes_size = fields.Integer("Index Size")
|
||||
historical_indexes_size = fields.Integer("Historical Index Size")
|
||||
|
||||
relations_size = fields.Integer("Many2many Tables Size")
|
||||
historical_relations_size = fields.Integer("Historical Many2many Tables Size")
|
||||
|
||||
tuples = fields.Integer("Estimated Rows")
|
||||
historical_tuples = fields.Integer("Historical Estimated Rows")
|
||||
|
||||
attachment_size = fields.Integer()
|
||||
historical_attachment_size = fields.Integer()
|
||||
|
||||
def action_open_model_sizes(self):
|
||||
"""Open the model_sizes from the report line.
|
||||
|
||||
At this point, the 'virtual' report record might not exist anymore
|
||||
so we fetch the dates from the context.
|
||||
"""
|
||||
self.ensure_one()
|
||||
domain = [
|
||||
("model", "=", self.model),
|
||||
(
|
||||
"measurement_date",
|
||||
"in",
|
||||
(
|
||||
self.env.context.get("measurement_date"),
|
||||
self.env.context.get("historical_measurement_date"),
|
||||
),
|
||||
),
|
||||
]
|
||||
action = self.env["ir.actions.actions"]._for_xml_id(
|
||||
"database_size.ir_model_size_action"
|
||||
)
|
||||
action["domain"] = domain
|
||||
return action
|
||||
|
||||
@api.model
|
||||
def _move_dates_to_context(self, domain):
|
||||
"""Move the requested comparison date from the domain into the context.
|
||||
|
||||
The values in the context will be used when creating the virtual table
|
||||
in `_table_query`.
|
||||
"""
|
||||
new_domain = []
|
||||
values = {}
|
||||
for clause in domain or []:
|
||||
for field in ("measurement_date", "historical_measurement_date"):
|
||||
if not isinstance(clause, tuple | list) or clause[0] != field:
|
||||
continue
|
||||
if field in values:
|
||||
raise UserError(
|
||||
self.env._(
|
||||
f"You cannot search on more than one value for {field} "
|
||||
"at the same time."
|
||||
)
|
||||
)
|
||||
if clause[1] in ("=", "==") and clause[2]:
|
||||
values[field] = clause[2]
|
||||
else:
|
||||
raise UserError(
|
||||
self.env._(
|
||||
f"Searching {field} for '{clause[1]} {clause[2]}' is "
|
||||
"not supported."
|
||||
)
|
||||
)
|
||||
new_domain.append((1, "=", 1))
|
||||
else:
|
||||
new_domain.append(clause)
|
||||
if values:
|
||||
self = self.with_context(**values)
|
||||
return self, new_domain
|
||||
|
||||
@api.model
|
||||
def _where_calc(self, domain, active_test=True):
|
||||
"""Move the requested dates from the domain into the context"""
|
||||
(self, new_domain) = self._move_dates_to_context(domain)
|
||||
return super()._where_calc(new_domain, active_test=active_test)
|
||||
|
||||
@api.model
|
||||
def search(self, domain, offset=0, limit=None, order=None):
|
||||
"""Move the requested dates from the domain into the context"""
|
||||
(self, new_domain) = self._move_dates_to_context(domain)
|
||||
return super().search(new_domain, offset=offset, limit=limit, order=order)
|
||||
|
||||
@api.model
|
||||
def search_count(self, domain, limit=None):
|
||||
"""Move the requested dates from the domain into the context"""
|
||||
(self, new_domain) = self._move_dates_to_context(domain)
|
||||
return super().search_count(new_domain, limit=limit)
|
||||
|
||||
@property
|
||||
def _table_query(self):
|
||||
"""Report comparative database size changes between two dates.
|
||||
|
||||
The dates are inserted in the context in this model's `search` method.
|
||||
"""
|
||||
measurement_date = self.env.context.get("measurement_date")
|
||||
if measurement_date:
|
||||
measurement_date = fields.Date.to_date(measurement_date)
|
||||
if not self.env["ir.model.size"].search(
|
||||
[("measurement_date", "=", measurement_date)],
|
||||
limit=1,
|
||||
):
|
||||
raise UserError(
|
||||
self.env._(
|
||||
"There is no data from "
|
||||
f"{fields.Date.to_string(measurement_date)}"
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Use the most recent measurement by default
|
||||
measurement_date = (
|
||||
self.env["ir.model.size"]
|
||||
.search([], order="id desc", limit=1)
|
||||
.measurement_date
|
||||
)
|
||||
if not measurement_date:
|
||||
raise UserError(self.env._("There does not seem to be any data"))
|
||||
|
||||
historical_measurement_date = self.env.context.get(
|
||||
"historical_measurement_date"
|
||||
)
|
||||
if historical_measurement_date:
|
||||
historical_measurement_date = fields.Date.to_date(
|
||||
historical_measurement_date
|
||||
)
|
||||
if not self.env["ir.model.size"].search(
|
||||
[("measurement_date", "=", historical_measurement_date)],
|
||||
limit=1,
|
||||
):
|
||||
raise UserError(
|
||||
self.env._(
|
||||
"There is no data from "
|
||||
f"{fields.Date.to_string(historical_measurement_date)}"
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Use last month by default
|
||||
last_month = measurement_date - relativedelta(months=1)
|
||||
historical_measurement_date = (
|
||||
self.env["ir.model.size"]
|
||||
.search(
|
||||
[
|
||||
("measurement_date", ">=", last_month),
|
||||
("measurement_date", "<", measurement_date),
|
||||
],
|
||||
order="measurement_date asc",
|
||||
limit=1,
|
||||
)
|
||||
.measurement_date
|
||||
)
|
||||
if not historical_measurement_date:
|
||||
raise UserError(
|
||||
self.env._("There does not seem to be enough data to compare")
|
||||
)
|
||||
|
||||
return self.env.cr.mogrify(
|
||||
"""
|
||||
select %(measurement_date)s as measurement_date,
|
||||
%(historical_measurement_date)s as historical_measurement_date,
|
||||
cur.id as id,
|
||||
cur.model,
|
||||
cur.model_name,
|
||||
cur.total_model_size,
|
||||
coalesce(hst.total_model_size, 0) as historical_total_model_size,
|
||||
coalesce(cur.total_model_size) - coalesce(hst.total_model_size, 0)
|
||||
as diff_total_model_size,
|
||||
|
||||
cur.total_database_size,
|
||||
coalesce(hst.total_database_size, 0) as historical_total_database_size,
|
||||
coalesce(cur.total_database_size, 0) - coalesce(hst.total_database_size, 0)
|
||||
as diff_total_database_size,
|
||||
|
||||
cur.table_size,
|
||||
coalesce(hst.table_size, 0) as historical_table_size,
|
||||
cur.total_table_size,
|
||||
coalesce(hst.total_table_size, 0) as historical_total_table_size,
|
||||
cur.indexes_size,
|
||||
coalesce(hst.indexes_size, 0) as historical_indexes_size,
|
||||
cur.relations_size,
|
||||
coalesce(hst.relations_size, 0) as historical_relations_size,
|
||||
cur.tuples,
|
||||
coalesce(hst.tuples, 0) as historical_tuples,
|
||||
cur.attachment_size,
|
||||
coalesce(hst.attachment_size, 0) as historical_attachment_size
|
||||
|
||||
from ir_model_size cur
|
||||
left join ir_model_size hst
|
||||
on cur.model = hst.model
|
||||
and hst.measurement_date = %(historical_measurement_date)s
|
||||
where cur.measurement_date = %(measurement_date)s
|
||||
""",
|
||||
{
|
||||
"measurement_date": measurement_date,
|
||||
"historical_measurement_date": historical_measurement_date,
|
||||
},
|
||||
).decode("utf-8")
|
||||
82
database_size/report/ir_model_size_report_views.xml
Executable file
82
database_size/report/ir_model_size_report_views.xml
Executable file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="ir_model_size_report_view_search" model="ir.ui.view">
|
||||
<field name="model">ir.model.size.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="measurement_date" />
|
||||
<field name="historical_measurement_date" />
|
||||
<field name="model" />
|
||||
<separator />
|
||||
<filter
|
||||
string="One Year Ago"
|
||||
name="filter_one_year_ago"
|
||||
domain="[('historical_measurement_date', '=', (context_today() - relativedelta(years=1)).strftime('%Y-%m-%d'))]"
|
||||
/>
|
||||
<filter
|
||||
string="One Month Ago"
|
||||
name="filter_one_month_ago"
|
||||
domain="[('historical_measurement_date', '=', (context_today() - relativedelta(months=1)).strftime('%Y-%m-%d'))]"
|
||||
/>
|
||||
<separator />
|
||||
<filter
|
||||
string="Notable Change"
|
||||
name="filter_diff_total_model_size"
|
||||
domain="[('diff_total_model_size', '>', 5)]"
|
||||
/>
|
||||
<separator />
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="ir_model_size_report_view_tree" model="ir.ui.view">
|
||||
<field name="model">ir.model.size.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<list
|
||||
editable="bottom"
|
||||
create="false"
|
||||
delete="false"
|
||||
edit="false"
|
||||
class="ir_model_size_wrap_header"
|
||||
>
|
||||
<field name="model" optional="hide" />
|
||||
<field name="model_name" />
|
||||
<field name="historical_measurement_date" />
|
||||
<field name="measurement_date" optional="hide" />
|
||||
<button
|
||||
name="action_open_model_sizes"
|
||||
type="object"
|
||||
icon="fa-arrow-right"
|
||||
title="Details"
|
||||
context="{'measurement_date': measurement_date, 'historical_measurement_date': historical_measurement_date}"
|
||||
/>
|
||||
<field name="historical_total_model_size" optional="hide" />
|
||||
<field name="total_model_size" optional="hide" />
|
||||
<field name="diff_total_model_size" />
|
||||
<field name="historical_total_database_size" optional="hide" />
|
||||
<field name="total_database_size" optional="hide" />
|
||||
<field name="diff_total_database_size" />
|
||||
<field name="historical_table_size" optional="hide" />
|
||||
<field name="table_size" optional="hide" />
|
||||
<field name="historical_indexes_size" optional="hide" />
|
||||
<field name="indexes_size" optional="hide" />
|
||||
<field name="historical_tuples" optional="hide" />
|
||||
<field name="tuples" optional="hide" />
|
||||
<field name="historical_attachment_size" optional="hide" />
|
||||
<field name="attachment_size" optional="hide" />
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="ir_model_size_report_action" model="ir.actions.act_window">
|
||||
<field name="name">Compare Database Size per Model</field>
|
||||
<field name="res_model">ir.model.size.report</field>
|
||||
<field name="view_mode">list</field>
|
||||
<field name="context">{"search_default_filter_diff_total_model_size": 1}</field>
|
||||
</record>
|
||||
<menuitem
|
||||
action="ir_model_size_report_action"
|
||||
id="ir_model_size_report_menu"
|
||||
name="Compare Size per Model"
|
||||
parent="database_size_menu"
|
||||
sequence="20"
|
||||
/>
|
||||
</odoo>
|
||||
5
database_size/security/ir.model.access.csv
Executable file
5
database_size/security/ir.model.access.csv
Executable file
@@ -0,0 +1,5 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_ir_model_size,access_ir_model_size,model_ir_model_size,base.group_system,1,1,0,0
|
||||
access_ir_model_index_size,access_ir_model_index_size,model_ir_model_index_size,base.group_system,1,1,0,0
|
||||
access_ir_model_size_report,access_ir_model_size_report,model_ir_model_size_report,base.group_system,1,0,0,0
|
||||
access_ir_model_relation_size,access_ir_model_relation_size,model_ir_model_relation_size,base.group_system,1,1,0,0
|
||||
|
BIN
database_size/static/description/icon.png
Executable file
BIN
database_size/static/description/icon.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
487
database_size/static/description/index.html
Executable file
487
database_size/static/description/index.html
Executable file
@@ -0,0 +1,487 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
|
||||
<title>README.rst</title>
|
||||
<style type="text/css">
|
||||
|
||||
/*
|
||||
:Author: David Goodger (goodger@python.org)
|
||||
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
|
||||
:Copyright: This stylesheet has been placed in the public domain.
|
||||
|
||||
Default cascading style sheet for the HTML output of Docutils.
|
||||
Despite the name, some widely supported CSS2 features are used.
|
||||
|
||||
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
|
||||
customize this style sheet.
|
||||
*/
|
||||
|
||||
/* used to remove borders from tables and images */
|
||||
.borderless, table.borderless td, table.borderless th {
|
||||
border: 0 }
|
||||
|
||||
table.borderless td, table.borderless th {
|
||||
/* Override padding for "table.docutils td" with "! important".
|
||||
The right padding separates the table cells. */
|
||||
padding: 0 0.5em 0 0 ! important }
|
||||
|
||||
.first {
|
||||
/* Override more specific margin styles with "! important". */
|
||||
margin-top: 0 ! important }
|
||||
|
||||
.last, .with-subtitle {
|
||||
margin-bottom: 0 ! important }
|
||||
|
||||
.hidden {
|
||||
display: none }
|
||||
|
||||
.subscript {
|
||||
vertical-align: sub;
|
||||
font-size: smaller }
|
||||
|
||||
.superscript {
|
||||
vertical-align: super;
|
||||
font-size: smaller }
|
||||
|
||||
a.toc-backref {
|
||||
text-decoration: none ;
|
||||
color: black }
|
||||
|
||||
blockquote.epigraph {
|
||||
margin: 2em 5em ; }
|
||||
|
||||
dl.docutils dd {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||
dl.docutils dt {
|
||||
font-weight: bold }
|
||||
*/
|
||||
|
||||
div.abstract {
|
||||
margin: 2em 5em }
|
||||
|
||||
div.abstract p.topic-title {
|
||||
font-weight: bold ;
|
||||
text-align: center }
|
||||
|
||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||
div.hint, div.important, div.note, div.tip, div.warning {
|
||||
margin: 2em ;
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||
div.important p.admonition-title, div.note p.admonition-title,
|
||||
div.tip p.admonition-title {
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||
div.danger p.admonition-title, div.error p.admonition-title,
|
||||
div.warning p.admonition-title, .code .error {
|
||||
color: red ;
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||
compound paragraphs.
|
||||
div.compound .compound-first, div.compound .compound-middle {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
div.compound .compound-last, div.compound .compound-middle {
|
||||
margin-top: 0.5em }
|
||||
*/
|
||||
|
||||
div.dedication {
|
||||
margin: 2em 5em ;
|
||||
text-align: center ;
|
||||
font-style: italic }
|
||||
|
||||
div.dedication p.topic-title {
|
||||
font-weight: bold ;
|
||||
font-style: normal }
|
||||
|
||||
div.figure {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
div.footer, div.header {
|
||||
clear: both;
|
||||
font-size: smaller }
|
||||
|
||||
div.line-block {
|
||||
display: block ;
|
||||
margin-top: 1em ;
|
||||
margin-bottom: 1em }
|
||||
|
||||
div.line-block div.line-block {
|
||||
margin-top: 0 ;
|
||||
margin-bottom: 0 ;
|
||||
margin-left: 1.5em }
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em ;
|
||||
border: medium outset ;
|
||||
padding: 1em ;
|
||||
background-color: #ffffee ;
|
||||
width: 40% ;
|
||||
float: right ;
|
||||
clear: right }
|
||||
|
||||
div.sidebar p.rubric {
|
||||
font-family: sans-serif ;
|
||||
font-size: medium }
|
||||
|
||||
div.system-messages {
|
||||
margin: 5em }
|
||||
|
||||
div.system-messages h1 {
|
||||
color: red }
|
||||
|
||||
div.system-message {
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.system-message p.system-message-title {
|
||||
color: red ;
|
||||
font-weight: bold }
|
||||
|
||||
div.topic {
|
||||
margin: 2em }
|
||||
|
||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||
margin-top: 0.4em }
|
||||
|
||||
h1.title {
|
||||
text-align: center }
|
||||
|
||||
h2.subtitle {
|
||||
text-align: center }
|
||||
|
||||
hr.docutils {
|
||||
width: 75% }
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||
clear: left ;
|
||||
float: left ;
|
||||
margin-right: 1em }
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||
clear: right ;
|
||||
float: right ;
|
||||
margin-left: 1em }
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left }
|
||||
|
||||
.align-center {
|
||||
clear: both ;
|
||||
text-align: center }
|
||||
|
||||
.align-right {
|
||||
text-align: right }
|
||||
|
||||
/* reset inner alignment in figures */
|
||||
div.align-right {
|
||||
text-align: inherit }
|
||||
|
||||
/* div.align-center * { */
|
||||
/* text-align: left } */
|
||||
|
||||
.align-top {
|
||||
vertical-align: top }
|
||||
|
||||
.align-middle {
|
||||
vertical-align: middle }
|
||||
|
||||
.align-bottom {
|
||||
vertical-align: bottom }
|
||||
|
||||
ol.simple, ul.simple {
|
||||
margin-bottom: 1em }
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal }
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha }
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha }
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman }
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman }
|
||||
|
||||
p.attribution {
|
||||
text-align: right ;
|
||||
margin-left: 50% }
|
||||
|
||||
p.caption {
|
||||
font-style: italic }
|
||||
|
||||
p.credits {
|
||||
font-style: italic ;
|
||||
font-size: smaller }
|
||||
|
||||
p.label {
|
||||
white-space: nowrap }
|
||||
|
||||
p.rubric {
|
||||
font-weight: bold ;
|
||||
font-size: larger ;
|
||||
color: maroon ;
|
||||
text-align: center }
|
||||
|
||||
p.sidebar-title {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold ;
|
||||
font-size: larger }
|
||||
|
||||
p.sidebar-subtitle {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
p.topic-title {
|
||||
font-weight: bold }
|
||||
|
||||
pre.address {
|
||||
margin-bottom: 0 ;
|
||||
margin-top: 0 ;
|
||||
font: inherit }
|
||||
|
||||
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
pre.code .ln { color: gray; } /* line numbers */
|
||||
pre.code, code { background-color: #eeeeee }
|
||||
pre.code .comment, code .comment { color: #5C6576 }
|
||||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||
|
||||
span.classifier {
|
||||
font-family: sans-serif ;
|
||||
font-style: oblique }
|
||||
|
||||
span.classifier-delimiter {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
span.interpreted {
|
||||
font-family: sans-serif }
|
||||
|
||||
span.option {
|
||||
white-space: nowrap }
|
||||
|
||||
span.pre {
|
||||
white-space: pre }
|
||||
|
||||
span.problematic, pre.problematic {
|
||||
color: red }
|
||||
|
||||
span.section-subtitle {
|
||||
/* font-size relative to parent (h1..h6 element) */
|
||||
font-size: 80% }
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docinfo {
|
||||
margin: 2em 4em }
|
||||
|
||||
table.docutils {
|
||||
margin-top: 0.5em ;
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
table.footnote {
|
||||
border-left: solid 1px black;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docutils td, table.docutils th,
|
||||
table.docinfo td, table.docinfo th {
|
||||
padding-left: 0.5em ;
|
||||
padding-right: 0.5em ;
|
||||
vertical-align: top }
|
||||
|
||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||
font-weight: bold ;
|
||||
text-align: left ;
|
||||
white-space: nowrap ;
|
||||
padding-left: 0 }
|
||||
|
||||
/* "booktabs" style (no vertical lines) */
|
||||
table.docutils.booktabs {
|
||||
border: 0px;
|
||||
border-top: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.docutils.booktabs * {
|
||||
border: 0px;
|
||||
}
|
||||
table.docutils.booktabs th {
|
||||
border-bottom: thin solid;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||
font-size: 100% }
|
||||
|
||||
ul.auto-toc {
|
||||
list-style-type: none }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document">
|
||||
|
||||
|
||||
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
|
||||
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
|
||||
</a>
|
||||
<div class="section" id="database-size">
|
||||
<h1>Database Size</h1>
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:57fdd3cd5e43a1434f9fe453728520f4dd445d90b738db359d34f40fa7d90328
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/server-tools/tree/18.0/database_size"><img alt="OCA/server-tools" src="https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-tools-18-0/server-tools-18-0-database_size"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
||||
<p>Monitor the size of your Odoo instance.</p>
|
||||
<p><strong>Table of contents</strong></p>
|
||||
<div class="contents local topic" id="contents">
|
||||
<ul class="simple">
|
||||
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
|
||||
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
|
||||
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
|
||||
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
|
||||
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
|
||||
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
|
||||
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="configuration">
|
||||
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
|
||||
<p>To configure this module, you can review the scheduled action called
|
||||
‘Take model size measurements’ and check the time at which you want it
|
||||
to run. It should only run once a day. If it runs more often, it just
|
||||
updates the existing set of sizes for the day.</p>
|
||||
<p>You may also review the Database Size settings in Odoo’s general
|
||||
settings and enable ‘Purge Older Model Size Measurements’. This task
|
||||
will by default delete most daily data older than a year except for the
|
||||
data captured on the first day of each month. These retention periods
|
||||
can be configured here as well.</p>
|
||||
</div>
|
||||
<div class="section" id="usage">
|
||||
<h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
|
||||
<p>You can use this module to keep an eye on the development of the size of
|
||||
your Odoo instance over time. Every day, a snapshot will be taken with
|
||||
the full size of the database and the attachments. You can query these
|
||||
daily snapshots, and you can compare the current size with a size at any
|
||||
date of the past for which there is data.</p>
|
||||
<p>Enable debug mode, then go to menu Settings -> Technical -> Database
|
||||
Size.</p>
|
||||
<p><img alt="image1" src="https://raw.githubusercontent.com/OCA/server-tools/18.0/database_size/static/images/model_size.png" /></p>
|
||||
<p>The data that is gathered and that is displayed are:</p>
|
||||
<ul class="simple">
|
||||
<li>Model Name - The name of the model to which the data is related</li>
|
||||
<li>Estimated Rows - The number of estimated rows according to the
|
||||
Postgresql query planner. For performance reasons, taking the data
|
||||
from the planner is preferred over doing an actual count, although the
|
||||
results may be imprecise.</li>
|
||||
<li>Bare Table Size - The disk usage of the model table without indexes
|
||||
etc.</li>
|
||||
<li>Index Size - The disk usage of the indexes in the model table.</li>
|
||||
<li>Many2many Tables Size - The disk usage of related many2many tables,
|
||||
including their indexes. To prevent double counts, many2many tables
|
||||
are only correlated with one of their tables (the largest of the two).</li>
|
||||
<li>Attachment Size - The disk usage of the attachments linked to the
|
||||
model records. Because Odoo will deduplicate attachments by content,
|
||||
attachments with the same content may be counted double in the
|
||||
attachment size of other models, but will not be counted double when
|
||||
linked to records of the same model more than once.</li>
|
||||
<li>Total Table Size - Bare Table Size + Index Size</li>
|
||||
<li>Total Database Size - Total Table Size + Many2many Tables Size</li>
|
||||
<li>Total Model Size - Total Database Size + Attachment Size</li>
|
||||
</ul>
|
||||
<p>If you click on individual records, you can inspect the sizes of each
|
||||
index and many2many table.</p>
|
||||
<p>All sizes are in megabytes.</p>
|
||||
<p>In the ‘Compare Size per Model’ report view, you can find these data
|
||||
twice: once for the selected measurement date (default: today), and once
|
||||
for the selected comparison date (default: one month ago).</p>
|
||||
<p><img alt="image2" src="https://raw.githubusercontent.com/OCA/server-tools/18.0/database_size/static/images/compare_model_size.png" /></p>
|
||||
<p>If you want to compare arbitrary dates, you can start typing the date in
|
||||
the search box. Be sure to enter the dates in the right format for your
|
||||
localization.</p>
|
||||
<p><img alt="image3" src="https://raw.githubusercontent.com/OCA/server-tools/18.0/database_size/static/images/select_date.png" /></p>
|
||||
</div>
|
||||
<div class="section" id="bug-tracker">
|
||||
<h2><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h2>
|
||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-tools/issues">GitHub Issues</a>.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/OCA/server-tools/issues/new?body=module:%20database_size%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||
</div>
|
||||
<div class="section" id="credits">
|
||||
<h2><a class="toc-backref" href="#toc-entry-4">Credits</a></h2>
|
||||
<div class="section" id="authors">
|
||||
<h3><a class="toc-backref" href="#toc-entry-5">Authors</a></h3>
|
||||
<ul class="simple">
|
||||
<li>Opener B.V.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="contributors">
|
||||
<h3><a class="toc-backref" href="#toc-entry-6">Contributors</a></h3>
|
||||
<ul class="simple">
|
||||
<li>Stefan Rijnhart <<a class="reference external" href="mailto:stefan@opener.amsterdam">stefan@opener.amsterdam</a>></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="maintainers">
|
||||
<h3><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h3>
|
||||
<p>This module is maintained by the OCA.</p>
|
||||
<a class="reference external image-reference" href="https://odoo-community.org">
|
||||
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
|
||||
</a>
|
||||
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.</p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/server-tools/tree/18.0/database_size">OCA/server-tools</a> project on GitHub.</p>
|
||||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
database_size/static/images/compare_model_size.png
Executable file
BIN
database_size/static/images/compare_model_size.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
BIN
database_size/static/images/model_size.png
Executable file
BIN
database_size/static/images/model_size.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
BIN
database_size/static/images/select_date.png
Executable file
BIN
database_size/static/images/select_date.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
13
database_size/static/src/scss/list_view_wrap_header.scss
Executable file
13
database_size/static/src/scss/list_view_wrap_header.scss
Executable file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
Apply word wrap to the model size list views table headers, and align the
|
||||
headers of table names to the left. This clarifies the various columns on
|
||||
these busy list views.
|
||||
*/
|
||||
.o_list_view.ir_model_size_wrap_header .o_list_renderer .o_list_table thead th {
|
||||
span.o_list_number_th {
|
||||
text-align: left;
|
||||
}
|
||||
span {
|
||||
text-wrap: wrap;
|
||||
}
|
||||
}
|
||||
1
database_size/tests/__init__.py
Executable file
1
database_size/tests/__init__.py
Executable file
@@ -0,0 +1 @@
|
||||
from . import test_database_size
|
||||
217
database_size/tests/test_database_size.py
Executable file
217
database_size/tests/test_database_size.py
Executable file
@@ -0,0 +1,217 @@
|
||||
# Copyright 2025 Opener B.V. <https://opener.amsterdam>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from datetime import timedelta
|
||||
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import fields
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests import TransactionCase, tagged
|
||||
|
||||
|
||||
@tagged("-at_install", "post_install")
|
||||
class TestDatabaseSize(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.partner_model = cls.env["ir.model"].search([("model", "=", "res.partner")])
|
||||
cls.today = fields.Date.context_today(cls.env.user)
|
||||
# Remove any data
|
||||
cls.env.cr.execute("delete from ir_model_size")
|
||||
|
||||
def test_database_size(self):
|
||||
"""Size table is populated and reports can be generated"""
|
||||
# Remove any data
|
||||
self.env.cr.execute("delete from ir_model_size")
|
||||
with self.assertRaisesRegex(UserError, "not.*any data"):
|
||||
self.env["ir.model.size.report"].search([])
|
||||
|
||||
self.env.ref(
|
||||
"database_size.ir_cron_ir_model_size_measure"
|
||||
).ir_actions_server_id.run()
|
||||
|
||||
# Backdate the data set
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
update ir_model_size
|
||||
set measurement_date = measurement_date - interval '10 days'
|
||||
"""
|
||||
)
|
||||
self.env["ir.model.size"].invalidate_model(["measurement_date"])
|
||||
|
||||
# Generate a new set
|
||||
self.env.ref(
|
||||
"database_size.ir_cron_ir_model_size_measure"
|
||||
).ir_actions_server_id.run()
|
||||
|
||||
# Retrieve the comparison
|
||||
report = self.env["ir.model.size.report"].search(
|
||||
[
|
||||
("model", "=", "res.partner"),
|
||||
("measurement_date", "=", self.today),
|
||||
("historical_measurement_date", "=", self.today - timedelta(days=10)),
|
||||
]
|
||||
)
|
||||
self.assertTrue(report)
|
||||
|
||||
# Run the action to open the details
|
||||
action = report.action_open_model_sizes()
|
||||
partner_sizes = self.env["ir.model.size"].search(
|
||||
[("model", "=", "res.partner")]
|
||||
)
|
||||
self.assertEqual(len(partner_sizes), 2)
|
||||
self.assertEqual(
|
||||
self.env[action["res_model"]].search(action["domain"]),
|
||||
partner_sizes,
|
||||
)
|
||||
|
||||
# Test default dates
|
||||
report2 = self.env["ir.model.size.report"].search(
|
||||
[("model", "=", "res.partner")]
|
||||
)
|
||||
# Default measurement date is the most recent date
|
||||
self.assertEqual(report2.measurement_date, self.today)
|
||||
# Default historical measurement date is the most recent date
|
||||
# within the last month
|
||||
self.assertEqual(
|
||||
report2.historical_measurement_date,
|
||||
self.today - timedelta(days=10),
|
||||
)
|
||||
|
||||
# Test missing data for date
|
||||
with self.assertRaisesRegex(UserError, "no data from"):
|
||||
self.env["ir.model.size.report"].search(
|
||||
[
|
||||
("model", "=", "res.partner"),
|
||||
(
|
||||
"historical_measurement_date",
|
||||
"=",
|
||||
self.today - timedelta(days=11),
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
def test_database_size_report_diff(self):
|
||||
"""Size report returns the expected values"""
|
||||
with self.assertRaisesRegex(UserError, "not.*any data"):
|
||||
self.env["ir.model.size.report"].search([])
|
||||
self.env.ref(
|
||||
"database_size.ir_cron_ir_model_size_measure"
|
||||
).ir_actions_server_id.run()
|
||||
self.env["ir.model.size"].flush_model()
|
||||
|
||||
# Forge some data for the partner model
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
update ir_model_size set
|
||||
total_database_size = coalesce(total_database_size, 0) + 10,
|
||||
total_model_size = coalesce(total_model_size, 0) + 5
|
||||
where model = 'res.partner' and measurement_date = %(self.today)s
|
||||
returning id;
|
||||
""",
|
||||
{"self.today": self.today},
|
||||
)
|
||||
|
||||
# Backdate the data set
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
update ir_model_size
|
||||
set measurement_date = measurement_date - interval '10 days'
|
||||
"""
|
||||
)
|
||||
|
||||
# Generate a new set
|
||||
self.env.ref(
|
||||
"database_size.ir_cron_ir_model_size_measure"
|
||||
).ir_actions_server_id.run()
|
||||
self.env["ir.model.size"].flush_model()
|
||||
|
||||
# Forge data for the partner model in the latest data set
|
||||
self.env.cr.execute(
|
||||
"""
|
||||
update ir_model_size set
|
||||
total_database_size = coalesce(total_database_size, 0) + 15,
|
||||
total_model_size = coalesce(total_model_size, 0) + 11
|
||||
where model = 'res.partner' and measurement_date = %(self.today)s
|
||||
returning id;
|
||||
""",
|
||||
{"self.today": self.today},
|
||||
)
|
||||
|
||||
# Retrieve the comparison
|
||||
report = self.env["ir.model.size.report"].search(
|
||||
[
|
||||
("model", "=", "res.partner"),
|
||||
("measurement_date", "=", self.today),
|
||||
("historical_measurement_date", "=", self.today - timedelta(days=10)),
|
||||
]
|
||||
)
|
||||
|
||||
# Size growth is indicated as expected
|
||||
self.assertEqual(report.diff_total_database_size, 5)
|
||||
self.assertEqual(report.diff_total_model_size, 6)
|
||||
|
||||
def test_database_size_purge(self):
|
||||
"""Records are purged according to their age"""
|
||||
|
||||
def purge(**args):
|
||||
with freeze_time("2025-01-01"):
|
||||
self.env["ir.model.size"]._purge()
|
||||
self.env["ir.model.size"].flush_model()
|
||||
|
||||
def create(date):
|
||||
return self.env["ir.model.size"].create(
|
||||
{
|
||||
"model": "__dummy",
|
||||
"measurement_date": date,
|
||||
}
|
||||
)
|
||||
|
||||
self.env["ir.config_parameter"].set_param("database_size.purge_enable", False)
|
||||
self.env["ir.config_parameter"].set_param(
|
||||
"database_size.retention_daily", False
|
||||
)
|
||||
self.env["ir.config_parameter"].set_param(
|
||||
"database_size.retention_monthly", False
|
||||
)
|
||||
|
||||
record20231201 = create("2023-12-01")
|
||||
record20231202 = create("2023-12-02")
|
||||
record20240101 = create("2024-01-01")
|
||||
record20240102 = create("2024-01-02")
|
||||
record20241201 = create("2024-12-01")
|
||||
record20241202 = create("2024-12-02")
|
||||
|
||||
# Purging is disabled
|
||||
purge()
|
||||
self.assertTrue(record20231202.exists())
|
||||
|
||||
# Enable purging
|
||||
self.env["ir.config_parameter"].set_param("database_size.purge_enable", True)
|
||||
|
||||
# By default, entries not on the first date of the month
|
||||
# and older than a year are purged
|
||||
purge()
|
||||
self.assertTrue(record20231201.exists())
|
||||
self.assertFalse(record20231202.exists())
|
||||
self.assertTrue(record20240101.exists())
|
||||
self.assertTrue(record20240102.exists())
|
||||
self.assertTrue(record20241201.exists())
|
||||
self.assertTrue(record20241202.exists())
|
||||
|
||||
self.env["ir.config_parameter"].set_param("database_size.retention_daily", 32)
|
||||
purge()
|
||||
self.assertTrue(record20231201.exists())
|
||||
self.assertTrue(record20240101.exists())
|
||||
self.assertFalse(record20240102.exists())
|
||||
self.assertTrue(record20241201.exists())
|
||||
self.assertTrue(record20241202.exists())
|
||||
|
||||
self.env["ir.config_parameter"].set_param(
|
||||
"database_size.retention_monthly", 366
|
||||
)
|
||||
purge()
|
||||
self.assertFalse(record20231201.exists())
|
||||
self.assertTrue(record20240101.exists())
|
||||
self.assertTrue(record20241201.exists())
|
||||
self.assertTrue(record20241202.exists())
|
||||
121
database_size/views/ir_model_size_views.xml
Executable file
121
database_size/views/ir_model_size_views.xml
Executable file
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="ir_model_size_view_search" model="ir.ui.view">
|
||||
<field name="model">ir.model.size</field>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<field name="model" />
|
||||
<group expand="0" string="Group By">
|
||||
<filter
|
||||
string="Date of Measurement"
|
||||
name="group_measurement_date"
|
||||
context="{'group_by': 'measurement_date:day'}"
|
||||
/>
|
||||
<filter
|
||||
string="Model"
|
||||
name="group_model"
|
||||
context="{'group_by': 'model'}"
|
||||
/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="ir_model_size_view_tree" model="ir.ui.view">
|
||||
<field name="model">ir.model.size</field>
|
||||
<field name="arch" type="xml">
|
||||
<list
|
||||
edit="false"
|
||||
create="false"
|
||||
delete="false"
|
||||
class="ir_model_size_wrap_header"
|
||||
>
|
||||
<field name="measurement_date" />
|
||||
<field name="model" optional="hide" />
|
||||
<field name="model_name" />
|
||||
<field name="total_model_size" />
|
||||
<field name="total_database_size" />
|
||||
<field name="total_table_size" optional="hide" />
|
||||
<field name="table_size" optional="hide" />
|
||||
<field name="indexes_size" optional="hide" />
|
||||
<field name="relations_size" optional="hide" />
|
||||
<field name="attachment_size" />
|
||||
<field name="tuples" />
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
<record id="ir_model_size_view_form" model="ir.ui.view">
|
||||
<field name="model">ir.model.size</field>
|
||||
<field name="arch" type="xml">
|
||||
<form edit="false" create="false" delete="false">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="measurement_date" style="margin-right: 1em;" />
|
||||
<field name="model" />
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="total_model_size" />
|
||||
<field name="total_database_size" />
|
||||
<field name="attachment_size" />
|
||||
<field name="tuples" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="total_table_size" />
|
||||
<field name="table_size" />
|
||||
<field name="indexes_size" />
|
||||
<field name="relations_size" />
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page
|
||||
name="indexes"
|
||||
string="Indexes"
|
||||
invisible="not ir_model_index_size_ids"
|
||||
>
|
||||
<field name="ir_model_index_size_ids" mode="list">
|
||||
<list editable="bottom">
|
||||
<field name="name" />
|
||||
<field name="size" />
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
<page
|
||||
name="relations"
|
||||
string="Many2many tables"
|
||||
invisible="not ir_model_relation_size_ids"
|
||||
>
|
||||
<field name="ir_model_relation_size_ids" mode="list">
|
||||
<list editable="bottom">
|
||||
<field name="name" />
|
||||
<field name="size" />
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="ir_model_size_action" model="ir.actions.act_window">
|
||||
<field name="name">Database Size per Model</field>
|
||||
<field name="res_model">ir.model.size</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="context">{'search_default_group_measurement_date': 1}</field>
|
||||
</record>
|
||||
<menuitem
|
||||
groups="base.group_system"
|
||||
id="database_size_menu"
|
||||
name="Database Size"
|
||||
parent="base.menu_custom"
|
||||
sequence="50"
|
||||
/>
|
||||
<menuitem
|
||||
action="ir_model_size_action"
|
||||
id="ir_model_size_menu"
|
||||
name="Size per Model"
|
||||
parent="database_size_menu"
|
||||
sequence="10"
|
||||
/>
|
||||
</odoo>
|
||||
35
database_size/views/res_config_settings_views.xml
Executable file
35
database_size/views/res_config_settings_views.xml
Executable file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="res_config_settings_view_form" model="ir.ui.view">
|
||||
<field name="name">Database Size Settings</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base_setup.res_config_settings_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<block name="performance" position="before">
|
||||
<block title="Database Size" id="database_size">
|
||||
<setting name="database_size_purge">
|
||||
<field name="database_size_purge" />
|
||||
</setting>
|
||||
<setting
|
||||
name="database_size_retention_daily"
|
||||
invisible="not database_size_purge"
|
||||
>
|
||||
<field name="database_size_retention_daily" class="oe_inline" />
|
||||
<span> days</span>
|
||||
</setting>
|
||||
<setting
|
||||
name="database_size_retention_monthly"
|
||||
invisible="not database_size_purge"
|
||||
help="Set to 0 to keep monthly measurements forever."
|
||||
>
|
||||
<field
|
||||
name="database_size_retention_monthly"
|
||||
class="oe_inline"
|
||||
/>
|
||||
<span> days</span>
|
||||
</setting>
|
||||
</block>
|
||||
</block>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user