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

feat: compute stats for the main material of the packaging #8662

Merged
merged 20 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
60 changes: 60 additions & 0 deletions docs/api/ref/api-v3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,66 @@ paths:
in: query
name: term
description: Alias for the "string" parameter provided for backward compatibility. "string" takes precedence.
'/api/v3/tag/{tagtype}/{tag_or_tagid}':
parameters:
- schema:
type: string
example: categories
name: tagtype
in: path
required: true
description: Type of the tag
- schema:
type: string
name: tag_or_tagid
in: path
required: true
description: 'Tag name (e.g. yogurts) or tag id (e.g. en:yogurts)'
get:
summary: Get knowledge panels for a tag
tags: []
responses:
'200':
description: OK
content:
application/json:
schema:
allOf:
- $ref: ./responses/response-status/response_status.yaml
- type: object
properties:
tagtype:
type: string
description: |
Input tagtype
tagid:
type: string
description: |
Input tagid
tag:
type: object
properties:
tagid:
type: string
description: Canonicalized tagid corresponding to the input tag_or_tagid
tagtype:
type: string
description: Canonicalized tagtype
knowledge_panels:
$ref: ./schemas/knowledge_panels/panels.yaml
description: Knowledge panels for the tag
application/xml:
schema:
type: object
properties: {}
operationId: get-api-v3-tag-tagtype-tag_or_tagid
description: |-
Return knowledge panels for a tag.

Currently the knowledge panels returned are:

Categories:
- Packaging stats for a category
tags:
- name: Read Requests
- name: Write Requests
2 changes: 1 addition & 1 deletion docs/api/ref/schemas/taxonomies/tagtype.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ enum:
- preservation
- states
- test
- allergens
- traces
- vitamins
description: 'Identifier of a taxonomy. See https://wiki.openfoodfacts.org/Global_taxonomies and https://github.com/openfoodfacts/openfoodfacts-server/tree/main/taxonomies'
examples: []
11 changes: 11 additions & 0 deletions lib/ProductOpener/API.pm
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ use ProductOpener::Packaging qw/:all/;

use ProductOpener::APIProductRead qw/:all/;
use ProductOpener::APIProductWrite qw/:all/;
use ProductOpener::APITagRead qw/:all/;
use ProductOpener::APITaxonomySuggestions qw/:all/;

use CGI qw(header);
Expand Down Expand Up @@ -397,6 +398,16 @@ sub process_api_request ($request_ref) {
add_invalid_method_error($response_ref, $request_ref);
}
}
# Tag read
elsif ($request_ref->{api_action} eq "tag") {

if ($request_ref->{api_method} =~ /^(GET|HEAD|OPTIONS)$/) {
read_tag_api($request_ref);
}
else {
add_invalid_method_error($response_ref, $request_ref);
}
}
# Unknown action
else {
$log->warn("process_api_request - unknown action", {request => $request_ref}) if $log->is_warn();
Expand Down
145 changes: 145 additions & 0 deletions lib/ProductOpener/APITagRead.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# This file is part of Product Opener.
#
# Product Opener
# Copyright (C) 2011-2023 Association Open Food Facts
# Contact: [email protected]
# Address: 21 rue des Iles, 94100 Saint-Maur des Fossés, France
#
# Product Opener is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

=head1 NAME

ProductOpener::APIProductRead - implementation of READ API for accessing product data

=head1 DESCRIPTION

=cut

package ProductOpener::APITagRead;

use ProductOpener::PerlStandards;
use Exporter qw< import >;

use Log::Any qw($log);

BEGIN {
use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS);
@EXPORT_OK = qw(
&read_tag_api
); # symbols to export on request
%EXPORT_TAGS = (all => [@EXPORT_OK]);
}

use vars @EXPORT_OK;

use ProductOpener::Config qw/:all/;
use ProductOpener::Display qw/:all/;
use ProductOpener::Users qw/:all/;
use ProductOpener::Lang qw/:all/;
use ProductOpener::API qw/:all/;
use ProductOpener::KnowledgePanels qw/:all/;
use ProductOpener::KnowledgePanelsTags qw/:all/;
use ProductOpener::Tags qw/:all/;

=head2 read_tag_api ( $request_ref )

Process API V3 READ tag requests.

Currently only return the knowledge_panels field for the tag, if any.

=head3 Parameters

=head4 $request_ref (input)

Reference to the request object.

=cut

sub read_tag_api ($request_ref) {

$log->debug("read_tag_api - start", {request => $request_ref}) if $log->is_debug();

my $response_ref = $request_ref->{api_response};

# we add the inputs to the response
my $tagtype = $request_ref->{tagtype};
my $tagid = $request_ref->{tagid};
stephanegigandet marked this conversation as resolved.
Show resolved Hide resolved

$response_ref->{tagtype} = $tagtype;
$response_ref->{tagid} = $tagid;

if (not defined $tagtype) {

add_error(
$response_ref,
{
message => {id => "missing_tagtype"},
field => {id => "tagtype", value => $tagtype},
impact => {id => "failure"},
}
);
$response_ref->{result} = {id => "tag_not_found"};
}

if (not defined $tagid) {

add_error(
$response_ref,
{
message => {id => "missing_tagid"},
field => {id => "tagid", value => $tagtype},
impact => {id => "failure"},
}
);
$response_ref->{result} = {id => "tag_not_found"};
}

# TODO: add check for valid tagtype? (we currently do not have a definitive list though)

if ((defined $tagtype) and (defined $tagid)) {
$response_ref->{result} = {id => "tag_found"};

# Canonicalize the tagid
my $canon_tagid;
if (defined $taxonomy_fields{$tagtype}) {
$canon_tagid = canonicalize_taxonomy_tag($lc, $tagtype, $tagid);

}
else {
my $display_tag = canonicalize_tag2($tagtype, $tagid);
$canon_tagid = get_string_id_for_lang("no_language", $display_tag);
}

# add canonical values to tag output
$response_ref->{tag} = {
stephanegigandet marked this conversation as resolved.
Show resolved Hide resolved
tagid => $canon_tagid,
tagtype => $tagtype
};

initialize_knowledge_panels_options($knowledge_panels_options_ref, $request_ref);
my $tag_ref = {}; # Object to store the knowledge panels
my $panels_created
= create_tag_knowledge_panels($tag_ref, $lc, $cc, $knowledge_panels_options_ref, $tagtype, $canon_tagid);

if ($panels_created) {
$response_ref->{tag}{knowledge_panels} = $tag_ref->{"knowledge_panels" . "_" . $lc};
}
}

$log->debug("read_tag_api - stop", {request => $request_ref}) if $log->is_debug();

return;
}

1;
6 changes: 4 additions & 2 deletions lib/ProductOpener/Ecoscore.pm
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ BEGIN {
%ecoscore_countries_enabled
@ecoscore_countries_enabled_sorted

%agribalyse

); # symbols to export on request
%EXPORT_TAGS = (all => [@EXPORT_OK]);
}
Expand All @@ -78,7 +80,7 @@ use Text::CSV();
use Math::Round;
use Data::DeepAccess qw(deep_get deep_exists);

my %agribalyse = ();
%agribalyse = ();

=head1 VARIABLES

Expand All @@ -102,7 +104,7 @@ foreach my $country (@ecoscore_countries_enabled_sorted) {

=head1 FUNCTIONS

=head2 load_agribalyse_data( $product_ref )
=head2 load_agribalyse_data()

Loads the AgriBalyse database.

Expand Down
47 changes: 44 additions & 3 deletions lib/ProductOpener/Packaging.pm
Original file line number Diff line number Diff line change
Expand Up @@ -950,10 +950,37 @@ sub aggregate_packaging_by_parent_materials ($product_ref) {
deep_val($packagings_materials_ref, "all", "weight") += $total_weight;
}
}
}

$product_ref->{packagings_materials} = $packagings_materials_ref;

return;
}

=head2 compute_weight_stats_for_parent_materials($product_ref)

Compute stats for the parent materials of a product:
- % of the weight of a material over the weight of all packaging
- weight of packaging per 100g of product

Also compute the main parent material.

=cut

sub compute_weight_stats_for_parent_materials ($product_ref) {

# We will determine which packaging material has the greatest weight
my $packagings_materials_main;
my $packagings_materials_main_weight = 0;

my $packagings_materials_ref = $product_ref->{packagings_materials};

if (defined $packagings_materials_ref) {

# Iterate over each parent material to compute weight statistics
my $total_weight = deep_get($packagings_materials_ref, "all", "weight");
foreach my $parent_material_ref (values %$packagings_materials_ref) {
foreach my $parent_material_id (sort keys %$packagings_materials_ref) {
my $parent_material_ref = $packagings_materials_ref->{$parent_material_id};
if (defined $parent_material_ref->{weight}) {
if ($total_weight) {
$parent_material_ref->{weight_percent} = $parent_material_ref->{weight} / $total_weight * 100;
Expand All @@ -962,12 +989,23 @@ sub aggregate_packaging_by_parent_materials ($product_ref) {
$parent_material_ref->{weight_100g}
= $parent_material_ref->{weight} / $product_ref->{product_quantity} * 100;
}
if ( ($parent_material_id ne "all")
and ($parent_material_ref->{weight} > $packagings_materials_main_weight))
{
$packagings_materials_main = $parent_material_id;
$packagings_materials_main_weight = $parent_material_ref->{weight};
}
alexgarel marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

$product_ref->{packagings_materials} = $packagings_materials_ref;

# Record the main packaging material
if (defined $packagings_materials_main) {
$product_ref->{packagings_materials_main} = $packagings_materials_main;
}
else {
delete $product_ref->{packagings_materials_main};
}
return;
}

Expand Down Expand Up @@ -1090,6 +1128,9 @@ sub analyze_and_combine_packaging_data ($product_ref, $response_ref) {
# Aggregate data per parent material
aggregate_packaging_by_parent_materials($product_ref);

# Compute stats for each parent material
compute_weight_stats_for_parent_materials($product_ref);

$log->debug("analyze_and_combine_packaging_data - done",
{packagings => $product_ref->{packagings}, response => $response_ref})
if $log->is_debug();
Expand Down
Loading
Loading