001/*
002 *  Copyright 2023 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.workspaces.statistics;
017
018import java.util.ArrayList;
019import java.util.List;
020import java.util.Map;
021
022import org.apache.avalon.framework.service.ServiceException;
023import org.apache.avalon.framework.service.ServiceManager;
024import org.apache.avalon.framework.service.Serviceable;
025
026import org.ametys.plugins.repository.AmetysObjectIterable;
027import org.ametys.plugins.workspaces.project.ProjectManager;
028import org.ametys.plugins.workspaces.project.modules.WorkspaceModule;
029import org.ametys.plugins.workspaces.project.modules.WorkspaceModuleExtensionPoint;
030import org.ametys.plugins.workspaces.project.objects.Project;
031import org.ametys.runtime.config.Config;
032import org.ametys.runtime.i18n.I18nizableText;
033import org.ametys.runtime.plugin.component.PluginAware;
034import org.ametys.runtime.plugins.admin.statistics.Statistics;
035import org.ametys.runtime.plugins.admin.statistics.StatisticsNode;
036import org.ametys.runtime.plugins.admin.statistics.StatisticsProvider;
037import org.ametys.runtime.plugins.admin.statistics.StatisticsValue;
038
039/**
040 * Send workspaces statistics
041 */
042public class WorkspacesStatisticsProvider implements StatisticsProvider, Serviceable, PluginAware
043{
044    private ProjectManager _projectManager;
045    private WorkspaceModuleExtensionPoint _workspaceModuleEP;
046    private String _id;
047
048    public void service(ServiceManager manager) throws ServiceException
049    {
050        _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE);
051        _workspaceModuleEP = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE);
052    }
053    
054    public void setPluginInfo(String pluginName, String featureName, String id)
055    {
056        _id = id;
057    }
058    
059    public Statistics getStatistics()
060    {
061        return new StatisticsNode(
062            _id,
063            new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_LABEL"),
064            "ametysicon-abecedary4",
065            null,
066            List.of(
067                _configStats(),
068                _projectsStats(),
069                _modulesStats()
070            ),
071            true
072        );
073    }
074
075    private StatisticsNode _configStats()
076    {
077        boolean configOOPreview = Config.getInstance().getValue("workspaces.onlyoffice.enabled", false, false);
078        boolean configOOEdit = configOOPreview && Config.getInstance().getValue("workspaces.onlyoffice.edition.enabled", false, false);
079        boolean configWebdav = Config.getInstance().getValue("workspaces.msoffice.enabled", false, false);
080        
081        return new StatisticsNode(
082            "config",
083            new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_CONFIG_LABEL"),
084            "ametysicon-gear39",
085            (configOOPreview ? 1 : 0) + (configOOEdit ? 1 : 0) + (configWebdav ? 1 : 0),
086            List.of(
087                new StatisticsValue(
088                    "oopreview",
089                    new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_CONFIG_OOPREVIEW_LABEL"),
090                    "ametysicon-code-html-picture62",
091                    configOOPreview
092                ),
093                new StatisticsValue(
094                    "ooedit",
095                    new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_CONFIG_OOEDIT_LABEL"),
096                    "ametysicon-editor-indent-more",
097                    configOOEdit
098                ),
099                new StatisticsValue(
100                    "webdav",
101                    new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_CONFIG_WEBDAV_LABEL"),
102                    "ametysicon-file-extension-doc",
103                    configWebdav
104                )
105            ),
106            false
107        );
108    }
109    
110    private StatisticsNode _modulesStats()
111    {
112        List<Map<String, Object>> projectsStatistics = _projectManager.getProjectsStatisticsForClientSide();
113        
114        List<Statistics> modulesStats = new ArrayList<>();
115        for (WorkspaceModule module : _workspaceModuleEP.getModules())
116        {
117            modulesStats.add(_moduleStat(projectsStatistics, module));
118        }
119        
120        return new StatisticsNode(
121            "modules",
122            new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_MODULES_LABEL"),
123            "ametysicon-puzzle-piece1",
124            null,
125            modulesStats,
126            true
127        );
128    }
129    
130    private Statistics _moduleStat(List<Map<String, Object>> projectsStatistics, WorkspaceModule module)
131    {
132        long totalActivated = 0;
133        
134        long totalSize = 0;
135        long maxSize = 0;
136        List<Long> sizes = new ArrayList<>();
137        
138        long totalCount = 0;
139        long maxCount = 0;
140        List<Long> counts = new ArrayList<>();
141        
142        for (Map<String, Object> oneProjectStatistics : projectsStatistics)
143        {
144            if ((Boolean) oneProjectStatistics.get(module.getModuleName() + "$activated"))
145            {
146                totalActivated++;
147                
148                long size = (Long) oneProjectStatistics.get(module.getModuleName() + "$size");
149                if (size > 0) // negative size are error messages
150                {
151                    totalSize += size;
152                    maxSize = Math.max(maxSize, size);
153                    sizes.add(size);
154                }
155                
156                String numberKey = oneProjectStatistics.keySet().stream().filter(k -> k.startsWith(module.getModuleName() + "$") && k.endsWith("_number")).findFirst().orElse(null);
157                if (numberKey != null)
158                {
159                    long count = ((Number) oneProjectStatistics.get(numberKey)).longValue();
160                    totalCount += count;
161                    maxCount = Math.max(maxCount, count);
162                    counts.add(count);
163                }
164            }
165        }
166        
167        counts.sort(null);
168        sizes.sort(null);
169        
170        return new StatisticsNode(
171            module.getModuleName(),
172            module.getModuleTitle(),
173            "ametysicon-puzzle33",
174            totalActivated,
175            List.of(
176                new StatisticsNode(
177                    "count",
178                    new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_MODULES_COUNT_LABEL"),
179                    "ametysicon-maths-abacus",
180                    totalCount,
181                    List.of(
182                        new StatisticsValue(
183                            "max",
184                            new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_MODULES_COUNT_MAX_LABEL"),
185                            "ametysicon-sort51",
186                            maxCount
187                        ),
188                        new StatisticsValue(
189                            "median",
190                            new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_MODULES_COUNT_MEDIAN_LABEL"),
191                            "ametysicon-maths-window-symbol-x",
192                            counts.size() > 0 ? counts.get(counts.size() / 2) : 0
193                        )
194                    ),
195                    false
196                ),
197                new StatisticsNode(
198                    "size",
199                    new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_MODULES_SIZE_LABEL"),
200                    "ametysicon-code-css-letter-spacing",
201                    totalSize,
202                    List.of(
203                        new StatisticsValue(
204                            "max",
205                            new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_MODULES_SIZE_MAX_LABEL"),
206                            "ametysicon-sort51",
207                            maxSize
208                        ),
209                        new StatisticsValue(
210                            "median",
211                            new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_MODULES_SIZE_MEDIAN_LABEL"),
212                            "ametysicon-maths-window-symbol-x",
213                            sizes.size() > 0 ? sizes.get(sizes.size() / 2) : 0
214                        )
215                    ),
216                    false
217                )
218            ),
219            false
220        );
221    }
222    
223    private StatisticsNode _projectsStats()
224    {
225        long nbPrivate = 0;
226        long nbModerated = 0;
227        long nbPublic = 0;
228           
229        try (AmetysObjectIterable<Project> projects = _projectManager.getProjects())
230        {
231            for (Project project : projects)
232            {
233                switch (project.getInscriptionStatus())
234                {
235                    case OPEN:
236                        nbPublic++;
237                        break;
238                    case MODERATED:
239                        nbModerated++;
240                        break;
241                    case PRIVATE:
242                        nbPrivate++;
243                        break;
244                    default:
245                        throw new IllegalArgumentException("Unknown inscriptionStatus " + project.getInscriptionStatus().toString() + " for project " + project.getId());
246                }
247            }
248        }
249        
250        return new StatisticsNode(
251            "count",
252            new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_PROJECTS_LABEL"),
253            "ametysicon-file98",
254            nbPrivate + nbModerated + nbPublic,
255            List.of(
256                new StatisticsValue(
257                    "open",
258                    new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_PROJECTS_OPEN_LABEL"),
259                    "ametysicon-body-group",
260                    nbPublic
261                ),
262                new StatisticsValue(
263                    "moderated",
264                    new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_PROJECTS_MODERATED_LABEL"),
265                    "ametysicon-body-people-tie",
266                    nbModerated
267                ),
268                new StatisticsValue(
269                    "private",
270                    new I18nizableText("plugin.workspaces", "PLUGINS_WORKSPACES_STATISTICS_PROJECTS_PRIVATE_LABEL"),
271                    "ametysicon-body-people",
272                    nbPrivate
273                )
274            ),
275            false
276        );
277    }
278}