001/*
002 *  Copyright 2026 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.repository.activities;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.Map.Entry;
023
024import javax.jcr.RepositoryException;
025
026import org.apache.avalon.framework.component.Component;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030
031import org.ametys.plugins.repository.AmetysObject;
032import org.ametys.plugins.repository.AmetysObjectIterable;
033import org.ametys.plugins.repository.AmetysObjectResolver;
034import org.ametys.runtime.i18n.I18nizableText;
035import org.ametys.runtime.plugin.component.AbstractLogEnabled;
036import org.ametys.runtime.plugins.admin.statistics.Statistics;
037import org.ametys.runtime.plugins.admin.statistics.StatisticsNode;
038import org.ametys.runtime.plugins.admin.statistics.StatisticsProvider;
039import org.ametys.runtime.plugins.admin.statistics.StatisticsValue;
040
041/**
042 * Provide different statistics related to the repository
043 */
044public class RepositoryStatisticsProvider extends AbstractLogEnabled implements StatisticsProvider, Component, Serviceable
045{
046    /** The ametys object resolver */
047    protected AmetysObjectResolver _resolver;
048    public void service(ServiceManager manager) throws ServiceException
049    {
050        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
051    }
052    
053    public Statistics getStatistics()
054    {
055        return new StatisticsNode("repository", new I18nizableText("Repository"), "ametysicon-trademark-jackrabbit", null, _computeActivitiesStatistics(), true);
056    }
057
058    private List<Statistics> _computeActivitiesStatistics()
059    {
060        try (AmetysObjectIterable<AmetysObject> activityHolders = _resolver.query("//element(*, ametys:activity-holder)"))
061        {
062            Map<String, List<Integer>> countByType = new HashMap<>();
063            
064            for (AmetysObject ao : activityHolders)
065            {
066                if (ao instanceof ActivityHolder activityHolder)
067                {
068                    Map<String, Integer> count = new HashMap<>();
069                    try (AmetysObjectIterable<Activity> activities = activityHolder.getActivities())
070                    {
071                        for (Activity activity : activities)
072                        {
073                            ActivityType activityType = activity.getActivityType();
074                            if (activityType != null)
075                            {
076                                count.merge(activityType.getId(), 1, (i, j) -> i + j);
077                            }
078                            else
079                            {
080                                getLogger().info("Activity '{}' has an unknown type '{}'", activity.getId(), activity.getValue(ActivityFactory.ACTIVITY_TYPE_ID));
081                            }
082                        }
083                    }
084                    catch (RepositoryException e)
085                    {
086                        getLogger().error("An error occurred while trying to compute statistics for {}", ao.getName(), e);
087                    }
088                    
089                    for (Entry<String, Integer> entry : count.entrySet())
090                    {
091                        List<Integer> activityCount = countByType.computeIfAbsent(entry.getKey(), k -> new ArrayList<>());
092                        activityCount.add(entry.getValue());
093                    }
094                }
095            }
096            
097            List<Statistics> statisticsByActivities = new ArrayList<>();
098            int grandTotal = 0;
099            for (String activityType : countByType.keySet())
100            {
101                List<Integer> countByHolder = countByType.get(activityType);
102                int size = countByHolder.size();
103                if (size > 0)
104                {
105                    countByHolder.sort(null);
106                    int median = countByHolder.get(size / 2);
107                    int max = countByHolder.getLast();
108                    int total = countByHolder.stream().mapToInt(i -> i).sum();
109                    
110                    grandTotal += total;
111                    
112                    List<Statistics> typeStats = List.of(
113                        new StatisticsValue("max", new I18nizableText("plugin.repository", "PLUGINS_REPOSITORY_STATISTICS_VALUE_MAXIMUM"), "ametysicon-sort51", max),
114                        new StatisticsValue("median", new I18nizableText("plugin.repository", "PLUGINS_REPOSITORY_STATISTICS_VALUE_MEDIAN"), "ametysicon-maths-window-symbol-x", median),
115                        new StatisticsValue("holderCount", new I18nizableText("plugin.repository", "PLUGINS_REPOSITORY_STATISTICS_VALUE_CONTAINER_COUNT"), "ametysicon-folder250", size)
116                    );
117                    statisticsByActivities.add(new StatisticsNode(activityType, new I18nizableText(activityType), "ametysicon-object-bell-black", total, typeStats, false));
118                }
119            }
120            
121            statisticsByActivities.sort((s1, s2) -> s1.getName().compareTo(s2.getName()));
122            
123            return List.of(
124                new StatisticsValue("activity-holder", new I18nizableText("plugin.repository", "PLUGINS_REPOSITORY_STATISTICS_ACTIVITY_HOLDER"), "ametysicon-folder250", activityHolders.getSize()),
125                new StatisticsNode("activities", new I18nizableText("plugin.repository", "PLUGINS_REPOSITORY_STATISTICS_ACTIVITIES"), "ametysicon-object-bell-black", grandTotal, statisticsByActivities, false)
126            );
127        }
128    }
129}