diff --git a/.eslintrc.js b/.eslintrc.js index cd3d330c8f..fe964b7348 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -22,6 +22,7 @@ module.exports = { "plugins": [ "react" ], + "ignorePatterns": ["**/static/**/*.js"], "rules": { "indent": "off", "no-empty-pattern": "off", @@ -42,5 +43,5 @@ module.exports = { "react": { "version": "detect" } - } + }, } diff --git a/rdmo/core/static/core/js/core.js b/rdmo/core/static/core/js/core.js index 3df2d3b9d4..0528df08fc 100644 --- a/rdmo/core/static/core/js/core.js +++ b/rdmo/core/static/core/js/core.js @@ -16,6 +16,13 @@ angular.module('core', ['ngResource']) method: 'PUT', params: {} }; + $resourceProvider.defaults.actions.postAction = { + method: 'POST', + params: { + id: '@id', + detail_action: '@detail_action' + } + }; }]) .filter('capitalize', function() { diff --git a/rdmo/projects/migrations/0059_project_progress.py b/rdmo/projects/migrations/0059_project_progress.py new file mode 100644 index 0000000000..abce63ea50 --- /dev/null +++ b/rdmo/projects/migrations/0059_project_progress.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.19 on 2023-08-24 09:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0058_meta'), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='progress_count', + field=models.IntegerField(help_text='The number of values for the progress bar.', null=True, verbose_name='Progress count'), + ), + migrations.AddField( + model_name='project', + name='progress_total', + field=models.IntegerField(help_text='The total number of expected values for the progress bar.', null=True, verbose_name='Progress total'), + ), + ] diff --git a/rdmo/projects/models/project.py b/rdmo/projects/models/project.py index 5b28682a4c..ec3b10ec25 100644 --- a/rdmo/projects/models/project.py +++ b/rdmo/projects/models/project.py @@ -14,7 +14,6 @@ from rdmo.views.models import View from ..managers import ProjectManager -from ..progress import get_progress class Project(MPTTModel, Model): @@ -62,6 +61,16 @@ class Project(MPTTModel, Model): verbose_name=_('Views'), help_text=_('The views that will be used for this project.') ) + progress_total = models.IntegerField( + null=True, + verbose_name=_('Progress total'), + help_text=_('The total number of expected values for the progress bar.') + ) + progress_count = models.IntegerField( + null=True, + verbose_name=_('Progress count'), + help_text=_('The number of values for the progress bar.') + ) class Meta: ordering = ('tree_id', 'level', 'title') @@ -83,21 +92,6 @@ def clean(self): 'parent': [_('A project may not be moved to be a child of itself or one of its descendants.')] }) - @property - def progress(self): - values, total = get_progress(self) - - try: - ratio = values / total - except ZeroDivisionError: - ratio = 0 - - return { - 'total': total, - 'values': values, - 'ratio': ratio - } - @property def catalog_uri(self): if self.catalog is not None: diff --git a/rdmo/projects/progress.py b/rdmo/projects/progress.py index 8e0693771a..6271c11bbd 100644 --- a/rdmo/projects/progress.py +++ b/rdmo/projects/progress.py @@ -6,7 +6,7 @@ from rdmo.questions.models import Catalog, Section, Page, QuestionSet, Question -def get_progress(project, snapshot=None): +def compute_progress(project, snapshot=None): # get all values for this project and snapshot project_values = project.values.filter(snapshot=snapshot).select_related('attribute', 'option') diff --git a/rdmo/projects/static/projects/js/project_questions/services.js b/rdmo/projects/static/projects/js/project_questions/services.js index 9d229da7e9..5df874cd95 100644 --- a/rdmo/projects/static/projects/js/project_questions/services.js +++ b/rdmo/projects/static/projects/js/project_questions/services.js @@ -933,13 +933,11 @@ angular.module('project_questions') } } else { // update progress - resources.projects.get({ + resources.projects.postAction({ id: service.project.id, detail_action: 'progress' }, function(response) { - if (service.progress.values != response.values) { - service.progress = response - } + service.progress = response }); // check if we need to refresh the site diff --git a/rdmo/projects/templates/projects/project_questions_progress.html b/rdmo/projects/templates/projects/project_questions_progress.html index 9b66e61bb9..1ae56dad41 100644 --- a/rdmo/projects/templates/projects/project_questions_progress.html +++ b/rdmo/projects/templates/projects/project_questions_progress.html @@ -3,8 +3,8 @@
- {% blocktrans trimmed with values='{$ service.progress.values $}' total='{$ service.progress.total $}' %} - {{ values }} of {{ total }} + {% blocktrans trimmed with count='{$ service.progress.count $}' total='{$ service.progress.total $}' %} + {{ count }} of {{ total }} {% endblocktrans %}
diff --git a/rdmo/projects/templates/projects/projects.html b/rdmo/projects/templates/projects/projects.html index 0edfaace36..b2aed864a1 100644 --- a/rdmo/projects/templates/projects/projects.html +++ b/rdmo/projects/templates/projects/projects.html @@ -124,6 +124,7 @@

{% trans 'My Projects' %}

{% trans 'Name' %} + {% trans 'Progress' %} {% trans 'Role' %} {% trans 'Last changed' %} @@ -138,6 +139,9 @@

{% trans 'My Projects' %}

{{ project.title }} + + {% project_progress project %} + {{ project.role|projects_role }} diff --git a/rdmo/projects/templates/projects/site_projects.html b/rdmo/projects/templates/projects/site_projects.html index f2a8f49c23..5caba675ed 100644 --- a/rdmo/projects/templates/projects/site_projects.html +++ b/rdmo/projects/templates/projects/site_projects.html @@ -61,7 +61,8 @@

{% blocktrans trimmed with site=request.site %}All projects on {{ site }}{% - + + @@ -76,6 +77,9 @@

{% blocktrans trimmed with site=request.site %}All projects on {{ site }}{% {{ project.title }} +

diff --git a/rdmo/projects/templatetags/projects_tags.py b/rdmo/projects/templatetags/projects_tags.py index 390a79e4f8..9517c7cda0 100644 --- a/rdmo/projects/templatetags/projects_tags.py +++ b/rdmo/projects/templatetags/projects_tags.py @@ -18,6 +18,19 @@ def projects_indent(level): return mark_safe('' + string + '') +@register.simple_tag() +def project_progress(project): + if project.progress_count is None or project.progress_total is None: + return '' + + try: + ratio = project.progress_count / project.progress_total + except ZeroDivisionError: + ratio = 0 + + return f'{ratio:.0%}' + + @register.filter() @stringfilter def projects_role(role): diff --git a/rdmo/projects/views/project.py b/rdmo/projects/views/project.py index 16050259d0..c3bcdef787 100644 --- a/rdmo/projects/views/project.py +++ b/rdmo/projects/views/project.py @@ -10,7 +10,6 @@ from django.shortcuts import get_object_or_404, redirect from django.urls import reverse_lazy from django.utils.decorators import method_decorator -from django.utils.http import urlencode from django.utils.translation import gettext_lazy as _ from django.views.decorators.csrf import ensure_csrf_cookie from django.views.generic import DeleteView, DetailView, TemplateView @@ -106,8 +105,8 @@ def get_context_data(self, **kwargs): context['number_of_filtered_projects'] = context["filter"].qs.count() context = set_context_querystring_with_filter_and_page(context) context['catalogs'] = Catalog.objects.filter_current_site() \ - .filter_group(self.request.user) \ - .filter_availability(self.request.user) + .filter_group(self.request.user) \ + .filter_availability(self.request.user) return context diff --git a/rdmo/projects/viewsets.py b/rdmo/projects/viewsets.py index fcf562af9e..eb3cf73a92 100644 --- a/rdmo/projects/viewsets.py +++ b/rdmo/projects/viewsets.py @@ -29,6 +29,7 @@ Project, Snapshot, Value) from .permissions import (HasProjectPagePermission, HasProjectPermission, HasProjectsPermission) +from .progress import compute_progress from .serializers.v1 import (IntegrationSerializer, InviteSerializer, IssueSerializer, MembershipSerializer, ProjectIntegrationSerializer, @@ -154,11 +155,26 @@ def options(self, request, pk=None): # if it didn't work return 404 raise NotFound() - @action(detail=True, permission_classes=(IsAuthenticated, )) + @action(detail=True, methods=['get', 'post'], permission_classes=(HasModelPermission | HasProjectPermission, )) def progress(self, request, pk=None): project = self.get_object() - project.catalog.prefetch_elements() - return Response(project.progress) + + if request.method == 'POST' or project.progress_count is None or project.progress_total is None: + # compute the progress and store + project.catalog.prefetch_elements() + project.progress_count, project.progress_total = compute_progress(project) + project.save() + + try: + ratio = project.progress_count / project.progress_total + except ZeroDivisionError: + ratio = 0 + + return Response({ + 'count': project.progress_count, + 'total': project.progress_total, + 'ratio': ratio + }) def perform_create(self, serializer): project = serializer.save(site=get_current_site(self.request))
{% trans 'Name' %}{% trans 'Name' %}{% trans 'Progress' %} {% trans 'Created' %} {% trans 'Last changed' %} + {% project_progress project %} + {{ project.created }}