GithubHelp home page GithubHelp logo

storage's Introduction

Runboat Pre-commit Status Build Status codecov Translation Status

storage

TODO: add repo description.

Available addons

addon version maintainers summary
fs_attachment 16.0.1.0.13 lmignon Store attachments on external object store
fs_base_multi_image 16.0.1.1.1 lmignon Mulitple Images from External File System
fs_base_multi_media 16.0.1.0.1 lmignon Give the possibility to store media data in external filesystem from odoo
fs_file 16.0.1.0.6 lmignon Field to store files into filesystem storages
fs_file_demo 16.0.1.0.0 Demo addon for fs_file and fs_image
fs_image 16.0.1.0.3 lmignon Field to store images into filesystem storages
fs_image_thumbnail 16.0.1.0.2 lmignon Generate and store thumbnail for images
fs_product_brand_multi_image 16.0.1.0.0 lmignon Link images to product brands
fs_product_multi_image 16.0.1.1.2 lmignon Manage multi images from extenal file system on product
fs_product_multi_media 16.0.1.0.2 lmignon Link media to products and categories
fs_storage 16.0.1.3.1 Implement the concept of Storage with amazon S3, sftp...
image_tag 16.0.1.0.1 Image tag model
storage_backend 16.0.1.0.1 Implement the concept of Storage with amazon S3, sftp...
storage_backend_sftp 16.0.1.0.0 Implement SFTP Storage
storage_file 16.0.1.0.0 Storage file in storage backend

Licenses

This repository is licensed under AGPL-3.0.

However, each module can have a totally different license, as long as they adhere to Odoo Community Association (OCA) policy. Consult each module's __manifest__.py file, which contains a license key that explains its license.


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.

storage's People

Contributors

acsonefho avatar adrienpeiffer avatar bealdav avatar benwillig avatar bguillot avatar danielduqma avatar etobella avatar florian-dacosta avatar grindtildeath avatar guewen avatar hparfr avatar hviorforgeflow avatar ivorra78 avatar jcoux avatar jordimforgeflow avatar leemannd avatar len-foss avatar lmignon avatar marielejeune avatar mymage avatar oca-git-bot avatar oca-travis avatar renatonlima avatar rousseldenis avatar sbejaoui avatar sbidoul avatar sebastienbeau avatar simahawk avatar vrenaville avatar weblate avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

storage's Issues

exisiting attachments migration

This is not documented anywhere I looked whatsoever, so whats our options to migrate existing attachments to an s3 object store ?

  • a button that will start the migration based on my configured setup

How does it work

Im trying to make the files get stored into a folder in the odoomachine and in the DirectoryPath i place it but then i list the contents of that folder and they are not there, i already did a chown 777 and still nothing

RFC block RPC calls

A long time ago I've made storage.backend api public on #75.
Some remarks where reported regarding exposing storage backend methods via RPC.
I've recently created a module to help protecting models from RPC calls: rpc_helper and now I'm adding the possibility to manage it via UI.

To improve the security here we could:

  1. recommend rpc_helper usage in the readme of storage_backend
  2. make storage_backend depend on rpc_helper and block all calls by default
  3. add a glue module (I don't think we need this given that you can configure via UI)

@lmignon @sebastienbeau @hparfr any opinion?

Module tries to add already existing columns when installing a second time

Module

fs_attachment

Describe the bug

This bug only exist when you are developing a new module on top of fs_attachment.

fs_attachment module in its pre_init_hook function creates the necessary columns to work. These columns are not removed when module installation is made (by user or by Odoo whenever another depending module could not be installed for example). This creates a very annoying problem in development mode: since columns are already created, fs_attachment module cannot be reinstalled (because it crashes when it reruns pre_init_hook to create already created columns). The only solution is to delete the columns manually or delete whole database and start from scratch.

In conclusion, fs_attachment pre_init_hook is not stateless, it should create the columns only if not done before on DB. This bug can also happen whenever you have already installed/uninstalled fs_attachment once and somewhere in the future user wants to reinstall it.

To Reproduce

Affected versions: v16.0.1.0.2

Steps to reproduce the behavior:

  1. Create a module depending on fs_attachment
  2. Create a pre_init_hook that does not work (raise exception for example)
  3. Launch odoo and install new module TWICE

Expected behavior
On the first launch, your new module should not be installed since it raises an exception.
On the second launch, fs_attachment tries to create columns that already exist (for example in following log: fs_storage_id already exists in ir_attachment).

Additional context
2024-02-08 09:12:27,399 10560 ERROR dev-archi-custom-project odoo.sql_db: bad query:
ALTER TABLE ir_attachment
ADD COLUMN fs_storage_id INTEGER;
ALTER TABLE ir_attachment
ADD FOREIGN KEY (fs_storage_id) REFERENCES fs_storage(id);

ERROR: ERREUR: la colonne « fs_storage_id » de la relation « ir_attachment » existe déjà

2024-02-08 09:12:27,408 10560 WARNING dev-archi-custom-project odoo.modules.loading: Transient module states were reset
2024-02-08 09:12:27,408 10560 ERROR dev-archi-custom-project odoo.modules.registry: Failed to load registry
Traceback (most recent call last):
File "repository\odoo\modules\registry.py", line 90, in new
odoo.modules.load_modules(registry, force_demo, status, update_module)
File "repository\odoo\modules\loading.py", line 488, in load_modules
processed_modules += load_marked_modules(cr, graph,
File "repository\odoo\modules\loading.py", line 372, in load_marked_modules
loaded, processed = load_module_graph(
File "repository\odoo\modules\loading.py", line 195, in load_module_graph
getattr(py_module, pre_init)(cr)
File "repository\oca-addons\fs_attachment\hooks.py", line 13, in pre_init_hook
cr.execute(
File "repository\odoo\sql_db.py", line 314, in execute
res = self._obj.execute(query, params)
psycopg2.errors.DuplicateColumn: ERREUR: la colonne « fs_storage_id » de la relation « ir_attachment » existe déjà

2024-02-08 09:12:27,415 10560 ERROR dev-archi-custom-project odoo.http: Exception during request handling.
Traceback (most recent call last):
File "repository\odoo\http.py", line 1998, in call
response = request._serve_db()
File "repository\odoo\http.py", line 1584, in _serve_db
return service_model.retrying(self._serve_ir_http, self.env)
File "repository\odoo\service\model.py", line 133, in retrying
result = func()
File "repository\odoo\http.py", line 1611, in _serve_ir_http
response = self.dispatcher.dispatch(rule.endpoint, args)
File "repository\odoo\http.py", line 1815, in dispatch
result = self.request.registry['ir.http']._dispatch(endpoint)
File "repository\odoo\addons\base\models\ir_http.py", line 154, in _dispatch
result = endpoint(**request.params)
File "repository\odoo\http.py", line 697, in route_wrapper
result = endpoint(self, *args, **params_ok)
File "repository\odoo\addons\web\controllers\dataset.py", line 46, in call_button
action = self._call_kw(model, method, args, kwargs)
File "repository\odoo\addons\web\controllers\dataset.py", line 33, in _call_kw
return call_kw(request.env[model], method, args, kwargs)
File "repository\odoo\api.py", line 461, in call_kw
result = _call_kw_multi(method, model, args, kwargs)
File "repository\odoo\api.py", line 448, in _call_kw_multi
result = method(recs, *args, **kwargs)
File "", line 2, in button_immediate_install
File "repository\odoo\addons\base\models\ir_module.py", line 76, in check_and_log
return method(self, *args, **kwargs)
File "repository\odoo\addons\base\models\ir_module.py", line 491, in button_immediate_install
return self._button_immediate_function(type(self).button_install)
File "repository\odoo\addons\base\models\ir_module.py", line 615, in _button_immediate_function
registry = modules.registry.Registry.new(self._cr.dbname, update_module=True)
File "", line 2, in new
File "repository\odoo\tools\func.py", line 87, in locked
return func(inst, *args, **kwargs)
File "repository\odoo\modules\registry.py", line 90, in new
odoo.modules.load_modules(registry, force_demo, status, update_module)
File "repository\odoo\modules\loading.py", line 488, in load_modules
processed_modules += load_marked_modules(cr, graph,
File "repository\odoo\modules\loading.py", line 372, in load_marked_modules
loaded, processed = load_module_graph(
File "repository\odoo\modules\loading.py", line 195, in load_module_graph
getattr(py_module, pre_init)(cr)
File "repository\oca-addons\fs_attachment\hooks.py", line 13, in pre_init_hook
cr.execute(
File "repository\odoo\sql_db.py", line 314, in execute
res = self._obj.execute(query, params)
psycopg2.errors.DuplicateColumn: ERREUR: la colonne « fs_storage_id » de la relation « ir_attachment » existe déjà

fs_product_multi_image from existing db

Is your feature request related to a problem?
Odoo generate product images (image_128, ..._256, 512, 1024, 1920) for its own usages in its modules and fs_storage modules may use images related to the same products for its own purposes.
How to conciliate these behaviors ?

Describe the solution you'd like
After a record is created in fs.product.image (adding an image in tab Images in product) resulting with specific_image res_field in ir.attachment we could also trigger the standard odoo way to generate images (_128 to _1920) to serve both usages ?

If this solution is right I could try to make a PR.

I just would like feedbacks.

Describe alternatives you've considered
I don't know any alternative to keep the 2 behaviors : the odoo way, and the storage way

Additional context
My request is not about migrate product image data from the odoo way to the storage way

cc @lmignon @benwillig @tuantrantg @sbidoul @sebastienbeau

[13.0] Dependency boto3 issue storage_backend_s3

Installing boto3 pip dependency with no version assigned is using version boto3==1.16.2
This version required urllib3>=1.25.4
requests pip package has a dependency that does not allow to use urllib3 version higher than 1.25

Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.6/site-packages (from requests->odoo==13.0) (2020.6.20)
Collecting urllib3<1.25,>=1.21.1
  Downloading urllib3-1.24.3-py2.py3-none-any.whl (118 kB)

Once you try to update storage_backend_s3 module you will get:

NameError: name 'boto3' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/odoo/custom/src/odoo/odoo/modules/registry.py", line 86, in new
    odoo.modules.load_modules(registry._db, force_demo, status, update_module)
  File "/opt/odoo/custom/src/odoo/odoo/modules/loading.py", line 419, in load_modules
    force, status, report, loaded_modules, update_module, models_to_check)
  File "/opt/odoo/custom/src/odoo/odoo/modules/loading.py", line 315, in load_marked_modules
    perform_checks=perform_checks, models_to_check=models_to_check
  File "/opt/odoo/custom/src/odoo/odoo/modules/loading.py", line 225, in load_module_graph
    load_data(cr, idref, mode, kind='data', package=package, report=report)
  File "/opt/odoo/custom/src/odoo/odoo/modules/loading.py", line 68, in load_data
    tools.convert_file(cr, package.name, filename, idref, mode, noupdate, kind, report)
  File "/opt/odoo/custom/src/odoo/odoo/tools/convert.py", line 736, in convert_file
    convert_xml_import(cr, module, fp, idref, mode, noupdate, report)
  File "/opt/odoo/custom/src/odoo/odoo/tools/convert.py", line 803, in convert_xml_import
    obj.parse(doc.getroot())
  File "/opt/odoo/custom/src/odoo/odoo/tools/convert.py", line 721, in parse
    exc_info[2]
  File "/opt/odoo/custom/src/odoo/odoo/tools/pycompat.py", line 13, in reraise
    raise value.with_traceback(tb)
  File "/opt/odoo/custom/src/odoo/odoo/tools/convert.py", line 712, in parse
    self._tag_root(de)
  File "/opt/odoo/custom/src/odoo/odoo/tools/convert.py", line 674, in _tag_root
    f(rec)
  File "/opt/odoo/custom/src/odoo/odoo/tools/convert.py", line 577, in _tag_record
    record = model._load_records([data], self.mode == 'update')
  File "/opt/odoo/custom/src/odoo/odoo/models.py", line 4104, in _load_records
    data['record']._load_records_write(data['values'])
  File "/opt/odoo/custom/src/odoo/odoo/addons/base/models/ir_ui_view.py", line 1374, in _load_records_write
    super(View, self)._load_records_write(values)
  File "/opt/odoo/custom/src/odoo/odoo/models.py", line 4042, in _load_records_write
    self.write(values)
  File "/opt/odoo/custom/src/odoo/odoo/addons/base/models/ir_ui_view.py", line 493, in write
    res = super(View, self).write(self._compute_defaults(vals))
  File "/opt/odoo/auto/addons/component_event/models/base.py", line 106, in write
    result = super(Base, self).write(vals)
  File "/opt/odoo/custom/src/odoo/odoo/models.py", line 3600, in write
    fields[0].determine_inverse(real_recs)
  File "/opt/odoo/custom/src/odoo/odoo/fields.py", line 1118, in determine_inverse
    getattr(records, self.inverse)()
  File "/opt/odoo/custom/src/odoo/odoo/addons/base/models/ir_ui_view.py", line 299, in _inverse_arch
    view.write(data)
  File "/opt/odoo/custom/src/odoo/odoo/addons/base/models/ir_ui_view.py", line 493, in write
    res = super(View, self).write(self._compute_defaults(vals))
  File "/opt/odoo/auto/addons/component_event/models/base.py", line 106, in write
    result = super(Base, self).write(vals)
  File "/opt/odoo/custom/src/odoo/odoo/models.py", line 3595, in write
    real_recs._validate_fields(set(vals) - set(inverse_fields))
  File "/opt/odoo/custom/src/odoo/odoo/models.py", line 1201, in _validate_fields
    check(self)
  File "/opt/odoo/custom/src/odoo/odoo/addons/base/models/ir_ui_view.py", line 394, in _check_xml
    self.postprocess_and_fields(view.model, view_doc, view.id)
  File "/opt/odoo/custom/src/odoo/odoo/addons/base/models/ir_ui_view.py", line 966, in postprocess_and_fields
    fields = Model.fields_get(None)
  File "/opt/odoo/custom/src/odoo/odoo/models.py", line 2805, in fields_get
    description = field.get_description(self.env)
  File "/opt/odoo/custom/src/odoo/odoo/fields.py", line 724, in get_description
    value = value(env)
  File "/opt/odoo/custom/src/odoo/odoo/fields.py", line 2225, in _description_selection
    return getattr(env[self.model_name], selection)()
  File "/opt/odoo/auto/addons/storage_backend_s3/models/storage_backend.py", line 65, in _selection_aws_region
    session = boto3.session.Session()
odoo.tools.convert.ParseError: "name 'boto3' is not defined" while parsing /opt/odoo/auto/addons/storage_backend_s3/views/backend_storage_view.xml:2, near
<odoo>
    <record id="storage_backend_view_form" model="ir.ui.view">
        <field name="model">storage.backend</field>
        <field name="inherit_id" ref="storage_backend.storage_backend_view_form"/>
        <field name="arch" type="xml">
            <group name="config" position="after">
                <group name="Amazon S3" string="Amazon S3" attrs="{'invisible': [('backend_type', '!=', 'amazon_s3')]}">
                    <group>
                        <field name="aws_host"/>
                        <field name="aws_access_key_id" attrs="{'required': [('backend_type', '=', 'amazon_s3')]}"/>
                        <field name="aws_secret_access_key" attrs="{'required': [('backend_type', '=', 'amazon_s3')], 'readonly':[('backend_type', '!=', 'amazon_s3')]}"/>
                        <field name="aws_bucket" attrs="{'required': [('backend_type', '=', 'amazon_s3')]}"/>
                        <field name="aws_cache_control"/>
                        <field name="aws_file_acl"/>
                    </group>
                    <group>
                        <field name="aws_region" widget="radio" attrs="{'required': [('backend_type', '=', 'amazon_s3')]}"/>
                        <field name="aws_other_region" attrs="{'invisible': [('aws_region', '!=', 'other')]}"/>
                    </group>
                </group>
            </group>
        </field>
    </record>
</odoo>

That error is raise when importing boto3 library:

Python 3.6.12 (default, Oct 13 2020, 21:45:01) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import boto3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/site-packages/boto3/__init__.py", line 16, in <module>
    from boto3.session import Session
  File "/usr/local/lib/python3.6/site-packages/boto3/session.py", line 17, in <module>
    import botocore.session
  File "/usr/local/lib/python3.6/site-packages/botocore/session.py", line 30, in <module>
    import botocore.credentials
  File "/usr/local/lib/python3.6/site-packages/botocore/credentials.py", line 34, in <module>
    from botocore.config import Config
  File "/usr/local/lib/python3.6/site-packages/botocore/config.py", line 16, in <module>
    from botocore.endpoint import DEFAULT_TIMEOUT, MAX_POOL_CONNECTIONS
  File "/usr/local/lib/python3.6/site-packages/botocore/endpoint.py", line 22, in <module>
    from botocore.awsrequest import create_request_object
  File "/usr/local/lib/python3.6/site-packages/botocore/awsrequest.py", line 25, in <module>
    import botocore.utils
  File "/usr/local/lib/python3.6/site-packages/botocore/utils.py", line 30, in <module>
    from urllib3.util.url import IPV6_ADDRZ_RE
ImportError: cannot import name 'IPV6_ADDRZ_RE'

Issue solved downgrading version to boto3==1.15.17

boto/botocore#2187

FYI @sebastienbeau @rvalyi @rousseldenis @simahawk

Use S3 Backend for public Website product

Is your feature request related to a problem?
I want to use those modules to override product image location to S3 Backend. Basically S3 backend is working from Settings menu and from Backoffice product, but Public Web return no image. I'm not sure if I'm missing something, but after inspecting various modules, it seems no one update product view on public web, so image is missing.

Describe the solution you'd like
Do we have a module that override public web, if not is this something planned to do?

Describe alternatives you've considered
I can try to do hack myself, but it will be probably less clean that modules you provide.

Access Forbidden

I am getting Access forbidden when saving an image. I have check permnisionns and I can save files to the /root/images directory

2022-11-04_12-39-26
2022-11-04_12-39-02

Filename len limitation, Yes or NO?

Some filestore have len limitation in file name
https://serverfault.com/questions/9546/filename-length-limits-on-linux/9548#9548
Currently we do not have any limitation in storage module and backend like S3 allow bigger name then what linux allow.
Maybe it can be a good idea to add a limitation to avoid issue when trying to move the storage from a backend A to a backend B.
Example all your image are on S3 then you when to move to your own solution and you realize that the name is too big and migration will be more complicated.
Having a limit (that can be unactivated if wanted) can be a good idea to make simpler the migration between differents solutions.

Storage Image widget not work

Module

The name of the module that has a bug.
Storage Image

Describe the bug

A clear and concise description of what the bug is.
set widget="storage_image_handle" not work
view and edit both display nothing.
image
image

To Reproduce

Affected versions:
odoo14.0
Storage Image 14.0.2.2.0
python3.9

appdirs==1.4.4
attrs==21.4.0
Babel==2.6.0
bcrypt==3.2.2
beautifulsoup4==4.11.1
cached-property==1.5.2
cachetools==5.2.0
certifi==2022.6.15
cffi==1.15.1
chardet==3.0.4
charset-normalizer==2.1.0
colorama==0.4.5
cryptography==37.0.2
decorator==4.3.0
defusedxml==0.7.1
docutils==0.14
ebaysdk==2.1.5
idna==3.3
isodate==0.6.1
Jinja2==3.1.2
libsass==0.17.0
lxml==4.9.0
MarkupSafe==2.1.1
mock==4.0.3
num2words==0.5.6
ofxparse==0.19
paramiko==2.11.0
passlib==1.7.1
Pillow==9.1.1
polib==1.1.0
psutil==5.9.1
psycopg2==2.9.3
pycparser==2.21
pydot==1.4.1
PyNaCl==1.5.0
pyparsing==3.0.9
PyPDF2==1.26.0
pyserial==3.4
python-dateutil==2.8.2
python-slugify==6.1.2
python-stdnum==1.8
pytz==2022.1
pyusb==1.0.2
pywin32==304
qrcode==6.1
reportlab==3.6.10
requests==2.28.1
requests-mock==1.9.3
requests-toolbelt==0.9.1
six==1.16.0
soupsieve==2.3.2.post1
text-unidecode==1.3
urllib3==1.26.9
vobject==0.9.6.1
Werkzeug==1.0.1
xlwt==1.3.0
zeep==3.2.0

Steps to reproduce the behavior:
1.install odoo
2.install Storage Image and Storage***
3.add model
`
class ImageUrl(models.Model):
_inherit = 'hr.employee'
_description = 'test'

image_url = fields.Char(string='image-url')   

`
image

4.add view

<odoo> <data> <record id="jack_test_form" model="ir.ui.view"> <field name="name">hr.employee.inherit.form</field> <field name="model">hr.employee</field> <field name="inherit_id" ref="hr.view_employee_form"/> <field name="arch" type="xml"> <xpath expr='//group' position="inside"> <group> <field name="image_url" widget="storage_image_handle"/> </group> </xpath> </field> </record> </data> </odoo>
image

Expected behavior
A clear and concise description of what you expected to happen.
show me a image and can edit image.

Additional context
Add any other context about the problem here. (e.g. OS, Python version, ...)
in test ,i run test_storage_image.py
if db_or_uri.startswith(('postgresql://', 'postgres://')):
AttributeError: 'bool' object has no attribute 'startswith'

i use sftp backup,in Technical test save image to sftp work ok.
del image , at the sftp not delete.
name and id sha maybe not all the solutions
The UUID timestamp should be part of the file name.
Users will always upload the same file name or even the same file.

Migration to version 16.0

Todo

https://github.com/OCA/maintainer-tools/wiki/Migration-to-version-16.0

Modules to migrate

BE careful!!! A complete refactoring is on this way by @sebastienbeau. The idea is to simplify the current approach and remove the dependency on component (that doesn't mean that it will no more be possible to use component with these addons but these addons will no more rely on components)

Missing module? Check https://github.com/OCA/maintainer-tools/wiki/%5BFAQ%5D-Missing-modules-in-migration-issue-list

S3 compatible endpoints?

Are these modules compatible with alternative S3 endpoints like WASABI, minio, digital ocean spaces etc...?
Or only Amazon AWS S3?
I couldn't find anything specific in the documentation about.
All of the above mentioned endpoints can use the amazon BOTO/BOTO3 API 1:1 out of the box.
The only requirement is having an option to point to a different endpoint/URL.

Any feedback on this please?

[14.0] Storage Image function is broken

In V14 an error occurs while trying to ad an image record.

Steps to reproduce
Go to Settings->Technical->Storage backend -> Image

The widget for uploading an image is unavalable.
When saving an record an error occurs

Error:
Odoo Server Error

Traceback (most recent call last):
  File "/.repo_requirements/odoo/odoo/addons/base/models/ir_http.py", line 237, in _dispatch
    result = request.dispatch()
  File "/.repo_requirements/odoo/odoo/http.py", line 683, in dispatch
    result = self._call_function(**self.params)
  File "/.repo_requirements/odoo/odoo/http.py", line 359, in _call_function
    return checked_call(self.db, *args, **kwargs)
  File "/.repo_requirements/odoo/odoo/service/model.py", line 94, in wrapper
    return f(dbname, *args, **kwargs)
  File "/.repo_requirements/odoo/odoo/http.py", line 347, in checked_call
    result = self.endpoint(*a, **kw)
  File "/.repo_requirements/odoo/odoo/http.py", line 912, in __call__
    return self.method(*args, **kw)
  File "/.repo_requirements/odoo/odoo/http.py", line 531, in response_wrap
    response = f(*args, **kw)
  File "/.repo_requirements/odoo/addons/web/controllers/main.py", line 1389, in call_kw
    return self._call_kw(model, method, args, kwargs)
  File "/.repo_requirements/odoo/addons/web/controllers/main.py", line 1381, in _call_kw
    return call_kw(request.env[model], method, args, kwargs)
  File "/.repo_requirements/odoo/odoo/api.py", line 394, in call_kw
    result = _call_kw_model_create(method, model, args, kwargs)
  File "/.repo_requirements/odoo/odoo/api.py", line 374, in _call_kw_model_create
    result = method(recs, *args, **kwargs)
  File "<decorator-gen-162>", line 2, in create
  File "/.repo_requirements/odoo/odoo/api.py", line 323, in _model_create_single
    return create(self, arg)
  File "/home/odoo/build/OCA/storage/setup/storage_image/odoo/addons/storage_image/models/storage_image.py", line 53, in create
    image = super(StorageImage, self).create(vals)
  File "<decorator-gen-159>", line 2, in create
  File "/.repo_requirements/odoo/odoo/api.py", line 323, in _model_create_single
    return create(self, arg)
  File "/home/odoo/build/OCA/storage/setup/storage_thumbnail/odoo/addons/storage_thumbnail/models/thumbnail_mixin.py", line 71, in create
    record = super().create(vals)
  File "<decorator-gen-65>", line 2, in create
  File "/.repo_requirements/odoo/odoo/api.py", line 344, in _model_create_multi
    return create(self, [arg])
  File "/.repo_requirements/odoo/odoo/addons/base/models/ir_fields.py", line 533, in create
    recs = super().create(vals_list)
  File "<decorator-gen-13>", line 2, in create
  File "/.repo_requirements/odoo/odoo/api.py", line 345, in _model_create_multi
    return create(self, arg)
  File "/.repo_requirements/odoo/odoo/models.py", line 3862, in create
    for data in parent_data_list
  File "<decorator-gen-65>", line 2, in create
  File "/.repo_requirements/odoo/odoo/api.py", line 345, in _model_create_multi
    return create(self, arg)
  File "/.repo_requirements/odoo/odoo/addons/base/models/ir_fields.py", line 533, in create
    recs = super().create(vals_list)
  File "<decorator-gen-13>", line 2, in create
  File "/.repo_requirements/odoo/odoo/api.py", line 345, in _model_create_multi
    return create(self, arg)
  File "/.repo_requirements/odoo/odoo/models.py", line 3901, in create
    fields[0].determine_inverse(batch_recs)
  File "/.repo_requirements/odoo/odoo/fields.py", line 1185, in determine_inverse
    getattr(records, self.inverse)()
  File "/home/odoo/build/OCA/storage/setup/storage_file/odoo/addons/storage_file/models/storage_file.py", line 118, in _inverse_data
    record.write(record._prepare_meta_for_file())
  File "/home/odoo/build/OCA/storage/setup/storage_file/odoo/addons/storage_file/models/storage_file.py", line 107, in _prepare_meta_for_file
    bin_data = base64.b64decode(self.data)
  File "/usr/lib/python3.6/base64.py", line 80, in b64decode
    s = _bytes_from_decode_data(s)
  File "/usr/lib/python3.6/base64.py", line 46, in _bytes_from_decode_data
    "string, not %r" % s.__class__.__name__) from None
Exception

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/.repo_requirements/odoo/odoo/http.py", line 639, in _handle_exception
    return super(JsonRequest, self)._handle_exception(exception)
  File "/.repo_requirements/odoo/odoo/http.py", line 315, in _handle_exception
    raise exception.with_traceback(None) from new_cause
TypeError: argument should be a bytes-like object or ASCII string, not 'bool'

image

Expected behaviour: Like in V12.
Widget for image uploading is displayed.
image

Migration to version 15.0

Todo

https://github.com/OCA/maintainer-tools/wiki/Migration-to-version-15.0

Modules to migrate

  • storage_backend #145
  • storage_backend_ftp - By @JasminSForgeFlow - #166
  • storage_backend_s3
  • storage_backend_sftp
  • storage_file
  • storage_image - By @JasminSForgeFlow - #190
  • storage_image_category_pos
  • storage_image_import
  • storage_image_product
  • storage_image_product_brand
  • storage_image_product_brand_import
  • storage_image_product_import
  • storage_image_product_pos
  • storage_media
  • storage_media_product
  • storage_thumbnail

Missing module? Check https://github.com/OCA/maintainer-tools/wiki/%5BFAQ%5D-Missing-modules-in-migration-issue-list

Wrong record rule for storage.image?

@sebastienbeau @lmignon this rule seems wrong to me:
https://github.com/OCA/storage/blob/12.0/storage_image/security/ir_rule.xml#L4-L13
it applies to storage.file instead of storage.image
The result is: as an admin - having image manager group - I cannot create a storage.file even if it has no file type.

2019-11-22 07:24:13,852 1 INFO odoodb odoo.models: Access Denied by record rules for operation: create on record ids: [179], uid: 2, model: storage.file 

if I disable it, I can create the file.

[17.0] fs_storage: Connection closed after some time

Hello!

Module: fs_storage

I think my issue is related to #319, but for V17.

I set up a FTP fs_storage, and it works great except that after some time (can be 10 seconds or a few minutes), it is impossible to communicate with the filesystem
When downloading:
image
When uploading:
image

If I restart my Odoo instance, it works again.

Like #319, I think the file system expires but is not renewed by Odoo.

Here, the filestore is saved in cache by Odoo.
When I remove the ormcache decorator, the filesystem is created every time, and I don't have any problems.

As mentionned in #319 and #320, with heavy traffic, it is not recommended to fetch the filesystem every time. As it is not my case, I will remove the ormcache decorator while looking for a permanent solution.

[16.0] Storage Addons Refactoring RFC

This RFC proposes a refactoring of the storage backend addons to make them based on Odoo standard model (ir.attachment) instead of custom model.

In the following, the storage_backend addon will be renamed in fs_storage and storage.backend will be renamed in fs.storage.

Motivation

In the storage_file mainly provides a custom model storage.file which is used to store the file content and metadata into a external file system.

Both share a lot of same fields. The not shared fields are mainly related to specific functionality provided by either storage.file or ir.attachment.

Here it's a table with the fields of the storage.file model and the corresponding field in the ir.attachment model:

storage.file ir.attachment
name name
url (computed) url
url_path (computed)
internal_url (computed)
slug (computed)
relative_path
file_size file_size
human_file_size file_size (computed)
checksum checksum
filename (computed)
extension (computed)
mimetype mimetype
data db_datas, raw (computed), datas (computed)
to_delete
active
company_id company_id
file_type
description
res_name
res_model
res_field
res_id
type
public
access_token
store_fname
index_content

The functional differences are:

  • storage.file allows to access to the file content via 2 URLs stored into the database: internal_url and url. The url is the public URL to access to the file content. The internal_url is the URL to access to the file content from the same 0doo's server. The url refers to the file into the external file system.
  • storage.file is used to transparently store the file content into an external file system. When you manually create a record in the storage.file you can choose the external file system where the file content will be stored.
  • storage.file is not transactional. (If a file is stored withing a transaction and the transaction is rolled back, the file will not be deleted from the external file system).
  • ir.attachment allows to store the file content into the Odoo's filestore.
  • ir.attachment is transactional. (If a file is stored withing a transaction and the transaction is rolled back, the file will be deleted from the filestore by a GC mechanism).
  • ir.attachment can be transparently used to store content from fields of type Binary. This field type comes with a specific UI to upload/download.

How can we reconcile both models?

fs_attachment

Since the main difference between both models is the place where the file content is stored, the idea is to extend the ir.attachment model to allow to store the file content into an external file system. With the last changes in the ir.attachment model, odoo provides now 3 methods that can be used to hook into the process of storing, retrieving and deleting the file content. This methods are:

  • _file_read
  • _file_write
  • _file_delete

Such an approach has already been implemented in addons from the camptocamp odoo-clout-platform repository: https://github.com/camptocamp/odoo-cloud-platform

The idea is to use the same approach here but using the api provided by the fsspec library through the new implementation of the storage_backend addon proposed in the pull request: #250

A new addon fs_attachment will be created to provide the glue between the ir.attachment model and the fs_storage addon. A new field code will be added to the fs.storage model to allow to choose the file system where the
file content will be stored. As it's done into the base_attachment_object_storage addon from C2C, it will be possible to globally configure the default file system where the file content will be stored for all the attachments.

fs_file

The storage.file addon will be replaced by the new fs_file addon. This addon will provide 2 new specialized field types: FSFile() and FSFileName.
These new field types will allow you to add fields on your models to store a file content into an external file system. Compared to the Binary field type, the FSFile() field will require 2 additional parameters:

  • storage_code: the code of the file system where the file content will be stored.
  • field_filename: the name of the FSFileName field.

The FSFileName field will be used to store the name of the file. It will be act as a related field on the name field of the ir.attachment record behind the FSFile() field.

To avoid useless resources consumption when the field content is retrieved to be displayed into the UI, the method convert_to_read will be overridden to return a url to use to download the file content.

A new JS Widget will be created to allow to upload/download the file content as an url. In the same spirit of minimizing the resource's consumption, this widget will not encode into base64 the file content when a new content is uploaded and put this content into the json document posted to odoo.
Instead, it will call a new controller to upload the file content and set as value of the field the new url when the form is saved prior to the submission of the form.

To avoid to pollute our file system with files uploaded but not linked to any record in the database due to a transaction rollback or some troubles when a form is submitted, a GC mechanism will be implemented to delete orphan files.

The implementation of the FSFile field will be base on the Binary field but will always put into the context the storage_code and field_filename to allows. Theses 2 parameters will be used by the fs_attachment addon to
select the file system where the file content will be stored and to set the name of the file.

fs_image

The storage.image addon will be replaced by the new fs_image addon. It will at least provides a new widget to allow the display of the image content from the url provided by the FSFile field. If it were not for the automatic thumbnail creation mechanism, this module could be summarised as the creation of 2 new fields type: FSImage() and FSImageAltName. The alt_name would be a related field to a new the alt_name field of the ir.attachment record behind. The FSImage field would be an extension of the FSFile field with 1 optional parameter: alt_name_field.

TO BE REFINED

Error when creating a new attachment

Module

fs_attachment

Describe the bug

New attachments cannot be created once the module is installed since it triggers the "_compute_internal_url" function, which crashes at the following line:

filename, extension = os.path.splitext(rec.name)

This occurs because the attachment initially lacks a name.

It can be fixed checking if rec.name. Else, rec.internal_url = "".

To Reproduce

Affected versions: 16.0.1.0.1

Steps to reproduce the behavior:

  1. Install fs_attachment
  2. Create a new attachment

Expected behavior
The attachment should be created with no errors.

Module does not work with S3 when deployed in Odoo docker container image

Module

fs_storage

Describe the bug

Prerequisite : Odoo is deployed with a Docker container and so running on Ubuntu image, fs_storage is installed, configuring a S3 storage like in development environment. Behavior is different between development and docker-deployed environment. When attaching an ir_attachment to S3, it either does not work or Odoo runs until timeout.

To Reproduce

Affected versions: v16.0.
Python: 3.9.
Odoo docker image: v16.0 from 01/01/2024.
fsspec: 2024.2.0

Steps to reproduce the behavior:

  1. Install docker image on web server (we use Scaleway provider to create an instance and install docker + docker-compose), contact me if any help is needed
  2. Install fs_storage module in Odoo and configure an S3 attachment storage for res.partner for example
  3. Add an attachment to a res.partner
  4. First try resolves to not adding the attachment at all (when refreshing with F5, attachment disappears). Second try, application runs with spinner indefinitely (until timeout)

Expected behavior
Attachment should be created in S3 mentioned directory.

Additional context
I use an Object storage (from Scaleway) (S3).

  • Connection test is successful
  • Tried with and without optimizes directory
  • Using a simple directory path "Archipell"
  • Tried every directory path

Migration to version 13.0

Todo

https://github.com/OCA/maintainer-tools/wiki/Migration-to-version-13.0

Modules to migrate

Missing module? Check https://github.com/OCA/maintainer-tools/wiki/%5BFAQ%5D-Missing-modules-in-migration-issue-list

Bug with storage_backend_ftp

Module

storage_backend_ftp

Describe the bug

I create new storage with one ftp (tested with filezilla)
I add new storage entry in odoo with this ftp
I save and click test (test say ok)
I try to insert file with this ftp storage selected
It seem form is uploading file but is hung
console log say:
/usr/lib/python3.7/ftplib.py(246)getresp()
raise error_perm(resp)

Error while installing

Im trying to install the storage module, but when i try to do it, theres an error, that says im missing the module 'component', the same happens wtih 'server_environment'
image
im probably misiing something ajajaj Ty guys

[13.0] Upgrade of storage modules on big database

Module

  • storage_thumbnail
  • storage_image

Describe the bug

During upgrade of these modules, some SQL Constraints are created/upgraded and some fields are created too.
That works fine on a little database with few products. But in my case I have arround 800 000 products and just change the ondelete cascade from ondelete restrict (#99) or store some computed fields (#94) just make the Odoo process killed.

To Reproduce

Affected versions:

  • 13.0
  • (maybe 14.0).

Steps to reproduce the behavior:

  1. Just upgrade these module (from previous version to the last one) with a database who contains 800 000 products.

Expected behavior
We shouldn't have to find and apply a script/SQL manually. It should be done into a migration script.
A solution could be to do:

  • pre-migration script to create columns in SQL (that avoid the first compute);
  • post-migration script to do the compute in background way. But we need a job for the background part (who is not a dependency of these modules).

Additional context
To fix this, I applied these 3 SQL queries manually:

  • ALTER TABLE category_image_relation DROP constraint category_image_relation_image_id_fkey, ADD CONSTRAINT category_image_relation_image_id_fkey foreign key (image_id) references storage_image(id) ON DELETE CASCADE;

  • ALTER TABLE product_product add column main_image_id int4 REFERENCES storage_image(id) ON DELETE SET NULL;

  • ALTER TABLE product_template add column main_image_id int4 REFERENCES storage_image(id) ON DELETE SET NULL;

Migration to version 17.0

Todo

https://github.com/OCA/maintainer-tools/wiki/Migration-to-version-17.0

Modules to migrate

Missing module? Check https://github.com/OCA/maintainer-tools/wiki/%5BFAQ%5D-Missing-modules-in-migration-issue-list

[14.0] Can not assign storage.backend to storage.backend.image

There is not possibility to select backend during the creation of an image in storage.backend.image.
image

Is this the correct behaviour? I soppose not because you can have different storage.backends, but I am not sure.

When I try to add it with a server action I get the next error:

Traceback (most recent call last):
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 640, in _handle_exception
    return super(JsonRequest, self)._handle_exception(exception)
  File "/opt/odoo/custom/src/odoo/odoo/http.py", line 316, in _handle_exception
    raise exception.with_traceback(None) from new_cause
botocore.errorfactory.NoSuchKey: An error occurred (NoSuchKey) when calling the GetObject operation: The specified key does not exist.

storage backend public api

storage.backend api is not public and to me makes little sense since to use the storage you must call those methods like _add_b64_data _ad_bin_data etc by guessing content encoding.

We should an api similar to adapters and call b64 or bin based on the input.
The final API would be:

  • add (or put or similar)
  • get (or read, because it returns the content of the file)
  • list (or list_dir or dir or similar)
  • delete

S3 issues

I was testing s3 connector against openstack.
I've come across few issues:

  • too many requests to get_available_regions
  • get available regions only works with AWS because no endpoint in get_available_regions()
  • endpoint can/should work with region

Documents app preview issue

Module

fs_attachment / fs_storage

Describe the bug

When using s3 storage for all attachments - unable to preview file (pdf, jpg, gif .... ) in Documents app
image

workaround

if I upload an attachment (pdf) to TASK - I can preview it inside the task
but if I try to preview the PDF via Documents app, throws error like on screenshot
copy from docker container error:

2023-12-13 17:20:25,651 15 ERROR odoo odoo.http: Exception during request handling. 
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/odoo/http.py", line 1988, in __call__
    response = request._serve_db()
  File "/usr/lib/python3/dist-packages/odoo/http.py", line 1584, in _serve_db
    return service_model.retrying(self._serve_ir_http, self.env)
  File "/usr/lib/python3/dist-packages/odoo/service/model.py", line 134, in retrying
    result = func()
  File "/usr/lib/python3/dist-packages/odoo/http.py", line 1611, in _serve_ir_http
    response = self.dispatcher.dispatch(rule.endpoint, args)
  File "/usr/lib/python3/dist-packages/odoo/http.py", line 1724, in dispatch
    return self.request.registry['ir.http']._dispatch(endpoint)
  File "/usr/lib/python3/dist-packages/odoo/addons/base/models/ir_http.py", line 154, in _dispatch
    result = endpoint(**request.params)
  File "/usr/lib/python3/dist-packages/odoo/http.py", line 699, in route_wrapper
    result = endpoint(self, *args, **params_ok)
  File "/usr/lib/python3/dist-packages/odoo/addons/web/controllers/binary.py", line 75, in content_common
    stream = request.env['ir.binary']._get_stream_from(record, field, filename, filename_field, mimetype)
  File "/usr/lib/python3/dist-packages/odoo/addons/base/models/ir_binary.py", line 129, in _get_stream_from
    stream = self._record_to_stream(record, field_name)
  File "/mnt/extra-addons/documents/models/ir_binary.py", line 11, in _record_to_stream
    return Stream.from_attachment(record.attachment_id.sudo())
  File "/usr/lib/python3/dist-packages/odoo/http.py", line 475, in from_attachment
    stat = os.stat(self.path)
FileNotFoundError: [Errno 2] No such file or directory: '/var/lib/odoo/filestore/odoo/abcd123a1:/so2023_3360347-2-31088-0.pdf'

For some reason, the Documents preview is looking into some local folder (which doesnt exist)

To Reproduce

Affected versions:
odoo 16
fs_attachment+fs_storage latest
documents app

Steps to reproduce the behavior:

  1. setup s3 for all attachments
  2. upload file (eg. pdf) to Documents app
  3. try to open the file in Documents app

Expected behavior
Open/preview the file (pdf, jpg...whatever)

S3 on AWS

I'm trying to set up storage for AWS S3 as the default for attachments, but it's not working.

I created an AWS user that can read and write to a bucket, generated the keys and configured the storage as follows:

code: s3
protocol: s3
options: {"key":"","secret":""}
Autovacuum Garbage Collection?: yes
Directory Path?
Use Filename Obfuscation?: yes

The _storage_file_write function in fs_attachment is set to create a subdirectory and in any case does not manage the bucket name in the file name.

What am I doing wrong?

Many Thank's

[16.0] fs_storage + EDI: connection closed after some time

Module

fs_storage

Describe the bug

I am using fs_storage to connect to SFTP with EDI (the PR is still not merged OCA/edi-framework#26). The connection works fine, but after some time the EDI jobs start to fail due to the following issue:

File "/opt/odoo/auto/addons/fs_storage/models/fs_storage.py", line 376, in add
    self.fs.makedirs(self.fs.sep.join(path))
  File "/usr/local/lib/python3.10/site-packages/fsspec/implementations/sftp.py", line 89, in makedirs
    self.ftp.mkdir(new_path, mode)
  File "/usr/local/lib/python3.10/site-packages/paramiko/sftp_client.py", line 460, in mkdir
    self._request(CMD_MKDIR, path, attr)
  File "/usr/local/lib/python3.10/site-packages/paramiko/sftp_client.py", line 856, in _request
    num = self._async_request(type(None), t, *args)
  File "/usr/local/lib/python3.10/site-packages/paramiko/sftp_client.py", line 881, in _async_request
    self._send_packet(t, msg)
  File "/usr/local/lib/python3.10/site-packages/paramiko/sftp.py", line 209, in _send_packet
    self._write_all(out)
  File "/usr/local/lib/python3.10/site-packages/paramiko/sftp.py", line 173, in _write_all
    n = self.sock.send(out)
  File "/usr/local/lib/python3.10/site-packages/paramiko/channel.py", line 799, in send
    return self._send(s, m)
  File "/usr/local/lib/python3.10/site-packages/paramiko/channel.py", line 1196, in _send
    raise socket.error("Socket is closed")
OSError: Socket is closed

If I press the button 'TEST CONNECTION' in the fs storage, I also get the same error. However, if I restart Odoo, it starts working fine again.

My hint is that the problem comes from fs_storage storing the connection in a variable (

self.__fs = self._get_filesystem()
). The connection closes at some point (probably due to timeout) and it does not work anymore.

@lmignon @sebastienbeau have you faced something like this with fs_storage?

RFC storage_backend: keep files unavailable while uploading (in ftp / sftp)

Case

Customer cases need to ensure that an uploading file in at least sftp/ftp (probably other backends) can't be consumed before to be fully uploaded.

Do you think this feature is generic ?

Should it be implemented in this repo ?

I think yes

Possible way to do it

It seems that the easier way to do it is to upload the file with a temporary name and when the file is completed, to rename it. Consumers of these files should have filters to exclude files like pattern argument in https://github.com/OCA/storage/blob/12.0/storage_backend/models/storage_backend.py#L46.
It's the only inconvenient, but I think it's acceptable

More sophisticated and flexible manners could be implemented if resource are available for it in a second time.

How to plug it in existing code

I don't have knowledge enough in this repo to find the better place

I wait for expert advices

What pattern to use to make files unavailable

  • use a dot prefix .myfile.xml
  • a suffix 'myfile.xml.tmp'
  • any other

Thanks to share your comments

@sebastienbeau @florian-dacosta @simahawk @lmignon @GSLabIt @Cedric-Pigeon

[13.0] Unmet dependencies: storage_backend_sftp

I've just pulled the latest 13.0 and it looks like storage_backend_sftp is not installable. When I add it as a dependency of my module, my installation fails with:

2020-05-12 18:45:48,719 32 INFO my_server odoo.modules.graph: module my_module: Unmet dependencies: storage_backend_sftp

When I navigate to Apps to install it manually, there is no install button (or icon):
Screen Shot 2020-05-12 at 2 51 22 PM

Any suggestions on how I can start chasing this down?

[RFC] modern formats like `.webp` and `.avif`

Is your feature request related to a problem?
No

Describe the solution you'd like
It would be interesting to extend the functionality of the image module to accept modern formats like .webp. I did not evaluate yet the difficulty of this but what do you think about this matter?

Describe alternatives you've considered
Extend the python decoder to accept modern formats like .webp.

Additional context

Instructions for how to use storage_backend_s3

Hi, I'm having trouble finding documentation on how to use storage_backend to send files to an s3 bucket. Is there any documentation out there on how to use this that I'm not finding? If so, can this be added to the readme file?

Documentation for getting storage_backend_s3 installed and running

I am facing issues setting up storage_backend_s3 module on my odoo 14 installation and I feel this might not be the ideal place for such a question, but I couldn't find anything on the readme or any external blog post with guidance on how to set these up.
Please let me know if you have a resource with installation instructions to get storage_backend_s3 setup on a odoo install.

I have got the module added to my addons path and installed, deleted the default filesystem StorageBackend object, and created a StorageBackend object in the database with the S3 bucket details.

But nothing happens after all this setup, Odoo continues to reach the local filesystem for the files even if I create a new db or add attachments.

Any help will be appreciated.

When directory_path is set the wrong protocol is used

If you set directory_path while using SFTP for instance, the fs object won't be the one of the protocol you've chosen.
In storage_backend you can set a default root path for all type of storages.

It will automatically - and w/o notice - use RootedDirFileSystem. This leads to broken implementations because it will raise NotImplementedError when trying to use sftp api.

It would be nice to support this in the future. Meanwhile we should probably ignore that param and/or hide it or raise warning.

To reproduce:

  1. add an sftp storage
  2. set a directory_path
  3. try to move a file
  4. kaboom

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.