Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Registry - Profile Photo #141

Merged
merged 10 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Available addons
addon | version | maintainers | summary
--- | --- | --- | ---
[g2p_bank](g2p_bank/) | 17.0.1.0.0 | | G2P Registry: Bank Details
[g2p_profile_image](g2p_profile_image/) | 17.0.1.0.0 | | OpenG2P Profile Image
[g2p_registry_addl_info](g2p_registry_addl_info/) | 17.0.1.0.0 | | G2P Registry: Additional Info
[g2p_registry_base](g2p_registry_base/) | 17.0.1.0.0 | | G2P Registry: Base
[g2p_registry_group](g2p_registry_group/) | 17.0.1.0.0 | | G2P Registry: Groups
Expand Down
52 changes: 52 additions & 0 deletions g2p_profile_image/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
=====================
OpenG2P Profile Image
=====================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:7bdf68b1fba30669d416a0928115edd95a9cfb1ee7091d4b593a0506ac530e20
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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/github-OpenG2P%2Fopeng2p--registry-lightgray.png?logo=github
:target: https://github.com/OpenG2P/openg2p-registry/tree/17.0-develop/g2p_profile_image
:alt: OpenG2P/openg2p-registry

|badge1| |badge2|

G2P Profile Image

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OpenG2P/openg2p-registry/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/OpenG2P/openg2p-registry/issues/new?body=module:%20g2p_profile_image%0Aversion:%2017.0-develop%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
~~~~~~~

* OpenG2P

Maintainers
~~~~~~~~~~~

This module is part of the `OpenG2P/openg2p-registry <https://github.com/OpenG2P/openg2p-registry/tree/17.0-develop/g2p_profile_image>`_ project on GitHub.

You are welcome to contribute.
1 change: 1 addition & 0 deletions g2p_profile_image/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
24 changes: 24 additions & 0 deletions g2p_profile_image/__manifest__.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using g2p_social_registry as a dependency will have issues in PBMS. Instead, you can use g2p_registry_base

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Part of OpenG2P Social Registry. See LICENSE file for full copyright and licensing details.
{
"name": "OpenG2P Profile Image",
"category": "G2P",
"version": "17.0.1.0.0",
"sequence": 1,
"author": "OpenG2P",
"website": "https://openg2p.org",
"license": "Other OSI approved licence",
"depends": [
"g2p_registry_base",
"g2p_documents",
],
"external_dependencies": {
"python": ["pillow==9.0.1"],
},
"data": [],
"assets": {},
"demo": [],
"images": [],
"application": True,
"installable": True,
"auto_install": False,
}
1 change: 1 addition & 0 deletions g2p_profile_image/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import profile_image, document_file
37 changes: 37 additions & 0 deletions g2p_profile_image/models/document_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from odoo import _, fields, models
from odoo.exceptions import UserError


class G2PDocumentFile(models.Model):
_inherit = "storage.file"
registrant_id = fields.Many2one("res.partner")

def create(self, vals):
if isinstance(vals, dict):
self._check_profile_tag(vals)
elif isinstance(vals, list):
for i in range(len(vals)):
vals_obj = vals[i]
self._check_profile_tag(vals_obj)

return super().create(vals)

def _check_profile_tag(self, vals_obj):
profile_tag = self.env["g2p.document.tag"].get_tag_by_name("Profile Image")
profile_tag_id = profile_tag.id
tags_ids = vals_obj.get("tags_ids", [])
has_profile_tag = any(tag[1] == profile_tag_id for tag in tags_ids)

if has_profile_tag:
existing_file = self.search(
[("registrant_id", "=", vals_obj.get("registrant_id")), ("tags_ids", "in", [profile_tag.id])],
limit=1,
)

if existing_file:
raise UserError(
_(
"Profile image already exists. To change your profile picture,"
"hover over your current image and click the edit button."
)
)
97 changes: 97 additions & 0 deletions g2p_profile_image/models/profile_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import base64
import io

from PIL import Image

from odoo import models
from odoo.exceptions import ValidationError


class G2PImageStorage(models.Model):
_inherit = "res.partner"

def write(self, values):
profile_tag = self.env["g2p.document.tag"].get_tag_by_name("Profile Image")
if not profile_tag:
profile_tag = self.env["g2p.document.tag"].create({"name": "Profile Image"})
storage_file = self.env["storage.file"].search(
[("registrant_id", "=", self.id), ("tags_ids", "in", [profile_tag.id])], limit=1
)

backend_id = self.env["storage.backend"].search([("name", "=", "Default S3 Document Store")], limit=1)

if values and values.get("image_1920") is not False:
img = values.get("image_1920", False)
if img:
binary_image = base64.b64decode(img)
image_size = len(binary_image)

if image_size > 1 * 1024 * 1024: # Image is > 1 MB
try:
# Scale down the image to be less than 1 MB
scaled_down_image = self._resize_image(binary_image)
values["image_1920"] = scaled_down_image

except Exception as e:
raise ValidationError(f"Error: {e}") from e

storage_file_vals = {
"data": img,
"file_size": image_size,
"name": "Profile Image",
"tags_ids": [(4, profile_tag.id)],
"registrant_id": self.id,
"backend_id": backend_id.id,
}

if storage_file:
storage_file.unlink()

self.env["storage.file"].create(storage_file_vals)
else:
if storage_file:
storage_file.unlink()
values["image_1920"] = img

else:
if values and storage_file:
storage_file.unlink()

result = super().write(values)
return result

def _resize_image(self, binary_image):
image_stream = io.BytesIO(binary_image)
image = Image.open(image_stream)
image_format = image.format
max_size = 1 * 1024 * 1024 # 1 MB

# resizing with thumbnail
image.thumbnail((1024, 1024), Image.ANTIALIAS)

buffered = io.BytesIO()
image.save(buffered, format=image_format, optimize=True, quality=85)

# size of the buffered image
if buffered.tell() <= max_size:
return base64.b64encode(buffered.getvalue())

for quality in range(80, 20, -10):
buffered = io.BytesIO()
image.save(buffered, format=image_format, optimize=True, quality=quality)
if buffered.tell() <= max_size:
return base64.b64encode(buffered.getvalue())

# If quality reduction is not enough, reduce dimensions further
new_width = image.size[0]
new_height = image.size[1]
while buffered.tell() > max_size and (new_width > 100 and new_height > 100):
new_width = int(new_width * 0.9)
new_height = int(new_height * 0.9)
image = image.resize((new_width, new_height), Image.ANTIALIAS)
buffered = io.BytesIO()
image.save(buffered, format=image_format, optimize=True, quality=85)
if buffered.tell() <= max_size:
break

return base64.b64encode(buffered.getvalue())
3 changes: 3 additions & 0 deletions g2p_profile_image/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
1 change: 1 addition & 0 deletions g2p_profile_image/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
G2P Profile Image
Loading