-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
367 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
__pycache__/ | ||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
============================= | ||
lcc_stats_api | ||
============================= | ||
|
||
Lokavaluto statistics module on Local Currency. | ||
It's part of Lokavaluto Project (https://lokavaluto.fr) | ||
|
||
Installation | ||
============ | ||
|
||
Just install lcc_stats_api, all dependencies | ||
will be installed by default. | ||
|
||
Known issues / Roadmap | ||
====================== | ||
|
||
Bug Tracker | ||
=========== | ||
|
||
Bugs are tracked on `GitHub Issues | ||
<https://github.com/Lokavaluto/lokavaluto-addons/issues>`_. In case of trouble, please | ||
check there if your issue has already been reported. If you spotted it first, | ||
help us smashing it by providing a detailed and welcomed feedback. | ||
|
||
Credits | ||
======= | ||
|
||
Images | ||
------ | ||
|
||
* Lokavaluto: `Icon <https://lokavaluto.fr/web/image/res.company/1/logo?unique=f3db262>`_. | ||
|
||
Contributors | ||
------------ | ||
|
||
* Stéphan SAINLEGER <https://github.com/stephansainleger> | ||
* Nicolas JEUDY <https://github.com/njeudy> | ||
* Lokavaluto Teams | ||
|
||
Funders | ||
------- | ||
|
||
The development of this module has been financially supported by: | ||
|
||
* Lokavaluto (https://lokavaluto.fr) | ||
* Mycéliandre (https://myceliandre.fr) | ||
* Elabore (https://elabore.coop) | ||
|
||
Maintainer | ||
---------- | ||
|
||
This module is maintained by LOKAVALUTO. | ||
|
||
LOKAVALUTO, is a nonprofit organization whose | ||
mission is to support the collaborative development of Odoo features and ecosystem for local complementary currency organizations. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from . import controllers | ||
|
||
from . import datamodel | ||
from . import services |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"name": "lcc_stats_api", | ||
"summary": "REST Odoo Backend for getting stats about local complementary currency", | ||
"author": "Lokavaluto", | ||
"website": "https://lokavaluto.fr", | ||
"category": "Website", | ||
"version": "12.0.1.0.0", | ||
# any module necessary for this one to work correctly | ||
"depends": [ | ||
"base", | ||
"base_rest", | ||
"auth_api_key", | ||
"base_rest_datamodel", | ||
"membership", | ||
"lcc_lokavaluto_app_connection", | ||
], | ||
# always loaded | ||
"data": [], | ||
# only loaded in demonstration mode | ||
"demo": [], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import rest_api |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from odoo.addons.base_rest.controllers import main | ||
|
||
|
||
class LokavalutoStatsPublicApiController(main.RestController): | ||
_root_path = "/lokavaluto_api/public/stats/" | ||
_collection_name = "lokavaluto.public.stats.services" | ||
_default_auth = "public" | ||
|
||
|
||
class LokavalutoStatsPrivateApiController(main.RestController): | ||
_root_path = "/lokavaluto_api/private/stats/" | ||
_collection_name = "lokavaluto.private.stats.services" | ||
_default_auth = "api_key" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import stats_filter |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from marshmallow import fields | ||
|
||
from odoo.addons.datamodel.core import Datamodel | ||
|
||
|
||
class StatsFilter(Datamodel): | ||
""" | ||
Describes stats API query parameters | ||
""" | ||
|
||
_name = "stats.filter" | ||
|
||
start_date = fields.Date( | ||
required=False, | ||
allow_none=True, | ||
description="date included, format YYYY-MM-DD", | ||
) | ||
end_date = fields.Date( | ||
required=False, | ||
allow_none=True, | ||
description="date included, format YYYY-MM-DD", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from . import public_stats_mlcc | ||
from . import private_stats_partner | ||
|
||
__api_version__ = 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
from typing_extensions import TypedDict | ||
|
||
from ..datamodel.stats_filter import StatsFilter | ||
|
||
|
||
# Generic stats about currency | ||
class CurrencyStats(TypedDict): | ||
eur_to_mlcc: float | ||
mlcc_to_eur: float | ||
mlcc_circulating: float | ||
|
||
|
||
# Generic validator for an API returning currency stats | ||
currency_stats_validator = { | ||
"eur_to_mlcc": {"type": "float", "required": True, "empty": False}, | ||
"mlcc_to_eur": {"type": "float", "required": True, "empty": False}, | ||
"mlcc_circulating": {"type": "float", "required": True, "empty": False}, | ||
} | ||
|
||
|
||
# Stats about partners participating to local currency | ||
class PartnersStats(TypedDict): | ||
nb_individuals: int | ||
nb_companies: int | ||
|
||
|
||
# validator for an API returning partners stats | ||
partners_stats_validator = { | ||
"nb_individuals": {"type": "integer", "required": True, "empty": False}, | ||
"nb_companies": {"type": "integer", "required": True, "empty": False}, | ||
} | ||
|
||
|
||
# Method to compute circulating currency | ||
def build_currency_stats_from_invoices( | ||
invoices_model, partner_id: int = None, stats_filter: StatsFilter = None | ||
) -> CurrencyStats: | ||
""" | ||
Build stats about MLCC from invoices | ||
""" | ||
|
||
# Look for paid invoices with LCC products | ||
domain_invoices = [ | ||
("state", "=", "paid"), | ||
("type", "in", ["out_invoice", "in_invoice"]), | ||
("has_numeric_lcc_products", "=", True), | ||
] | ||
|
||
# Filter for a specific partner if specified | ||
if partner_id: | ||
domain_invoices.append(("partner_id", "=", partner_id)) | ||
|
||
# Filter date if specified | ||
if stats_filter and stats_filter.start_date: | ||
domain_invoices.append(("date_invoice", ">=", stats_filter.start_date)) | ||
|
||
if stats_filter and stats_filter.end_date: | ||
domain_invoices.append(("date_invoice", "<=", stats_filter.end_date)) | ||
|
||
invoices = invoices_model.search(domain_invoices) | ||
mlcc_to_eur = 0.00 | ||
eur_to_mlcc = 0.00 | ||
for invoice in invoices: | ||
if invoice.type == "out_invoice": | ||
eur_to_mlcc += invoice.amount_total | ||
else: | ||
mlcc_to_eur += invoice.amount_total | ||
|
||
return CurrencyStats( | ||
eur_to_mlcc=eur_to_mlcc, | ||
mlcc_to_eur=mlcc_to_eur, | ||
mlcc_circulating=eur_to_mlcc - mlcc_to_eur, | ||
) | ||
|
||
|
||
# Method to compute the number of partners participating in the currency | ||
def build_currency_partners_stats(partners_model) -> PartnersStats: | ||
# Search active members only | ||
domain_partners = [ | ||
( | ||
"membership_state", | ||
"in", | ||
["invoiced", "paid", "free"], | ||
), | ||
("is_main_profile", "=", True), | ||
("active", "=", True), | ||
] | ||
|
||
partners = partners_model.search(domain_partners) | ||
|
||
individuals = len([p for p in partners if p.is_company == False]) | ||
companies = len(partners) - individuals | ||
|
||
return PartnersStats(nb_individuals=individuals, nb_companies=companies) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import logging | ||
|
||
from odoo.exceptions import AccessError | ||
from odoo.addons.base_rest import restapi | ||
from odoo.addons.base_rest_datamodel.restapi import Datamodel | ||
from odoo.addons.component.core import Component | ||
from odoo.http import request | ||
|
||
from .build_stats import ( | ||
CurrencyStats, | ||
build_currency_stats_from_invoices, | ||
currency_stats_validator, | ||
) | ||
from ..datamodel.stats_filter import StatsFilter | ||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
|
||
class PrivateStatsPartnerService(Component): | ||
_inherit = "base.rest.service" | ||
_name = "private_stats_partner.service" | ||
_usage = "partner" | ||
_collection = "lokavaluto.private.stats.services" | ||
_description = """ | ||
MLCC Partner Private Stats Services | ||
Get private statistics of a local currency partner. | ||
""" | ||
|
||
# The following method are 'public' and can be called from the controller. | ||
|
||
@restapi.method( | ||
[(["/<int:id>/get", "/<int:id>"], "GET")], | ||
input_param=Datamodel("stats.filter"), | ||
) | ||
def get(self, _id, stats_filter: StatsFilter) -> CurrencyStats: | ||
""" | ||
Get a partner MLCC stats. | ||
""" | ||
|
||
# Check that user is accessing its own data | ||
user_api_key = request.httprequest.headers["Api-Key"] | ||
user = ( | ||
self.env["auth.api.key"] | ||
.sudo() | ||
.search([("key", "=", user_api_key)], limit=1) | ||
) | ||
partner = ( | ||
self.env["res.partner"] | ||
.sudo() | ||
.search([("odoo_user_id", "=", user.user_id.id)]) | ||
) | ||
if _id != partner.id and _id != partner.public_profile_id.id: | ||
raise AccessError( | ||
f"{partner.name} not allowed to access data of partner ID {_id}" | ||
) | ||
|
||
# Get currency stats based on partner's invoices | ||
currency_stats: CurrencyStats = build_currency_stats_from_invoices( | ||
self.env["account.invoice"], partner_id=_id, stats_filter=stats_filter | ||
) | ||
|
||
return currency_stats | ||
|
||
########################################################## | ||
# Validators | ||
########################################################## | ||
def _validator_return_get(self): | ||
res = { | ||
**currency_stats_validator, | ||
} | ||
return res | ||
|
||
def _get_openapi_default_parameters(self): | ||
defaults = super( | ||
PrivateStatsPartnerService, self | ||
)._get_openapi_default_parameters() | ||
defaults.append( | ||
{ | ||
"name": "API-KEY", | ||
"in": "header", | ||
"description": "Auth API key - get a token using API /lokavaluto_api/public/auth/authenticate", | ||
"required": True, | ||
"schema": {"type": "string"}, | ||
"style": "simple", | ||
} | ||
) | ||
|
||
return defaults |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import logging | ||
|
||
from odoo.addons.base_rest import restapi | ||
from odoo.addons.base_rest_datamodel.restapi import Datamodel | ||
from odoo.addons.component.core import Component | ||
|
||
from .build_stats import ( | ||
build_currency_stats_from_invoices, | ||
CurrencyStats, | ||
currency_stats_validator, | ||
PartnersStats, | ||
build_currency_partners_stats, | ||
partners_stats_validator, | ||
) | ||
from ..datamodel.stats_filter import StatsFilter | ||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
|
||
class MlccStats(CurrencyStats): | ||
nb_individuals: int | ||
nb_companies: int | ||
|
||
|
||
class PublicStatsMlccService(Component): | ||
_inherit = "base.rest.service" | ||
_name = "public_stats.service" | ||
_usage = "mlcc" | ||
_collection = "lokavaluto.public.stats.services" | ||
_description = """ | ||
MLCC Public Stats Services | ||
Get public statistics about a local currency | ||
""" | ||
|
||
# The following method are 'public' and can be called from the controller. | ||
|
||
@restapi.method( | ||
[(["/get", "/"], "GET")], | ||
input_param=Datamodel("stats.filter"), | ||
) | ||
def get(self, stats_filter: StatsFilter) -> MlccStats: | ||
""" | ||
Get MLCC public stats. | ||
""" | ||
|
||
# Get currency stats based on all invoices | ||
currency_stats: CurrencyStats = build_currency_stats_from_invoices( | ||
self.env["account.invoice"].sudo(), stats_filter=stats_filter | ||
) | ||
|
||
# Get partners stats | ||
partner_stats: PartnersStats = build_currency_partners_stats( | ||
self.env["res.partner"].sudo() | ||
) | ||
return MlccStats(**currency_stats, **partner_stats) | ||
|
||
########################################################## | ||
# Validators | ||
########################################################## | ||
def _validator_return_get(self): | ||
res = {**currency_stats_validator, **partners_stats_validator} | ||
return res |