Skip to content

Commit

Permalink
wip before hash
Browse files Browse the repository at this point in the history
  • Loading branch information
a-belhadj committed Dec 5, 2023
1 parent 7d8abc6 commit 6a178cf
Show file tree
Hide file tree
Showing 25 changed files with 532 additions and 112 deletions.
2 changes: 1 addition & 1 deletion Squest/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
DATE_FORMAT = os.environ.get('DATE_FORMAT', "%d %b, %Y %H:%M")
LOGIN_HELPER_TEXT = os.environ.get('LOGIN_HELPER_TEXT', None)
IS_DEV_SERVER = str_to_bool(os.environ.get('IS_DEV_SERVER', False))
SQL_DEBUG = str_to_bool(os.environ.get('SQL_DEBUG', False))
SQL_DEBUG = str_to_bool(os.environ.get('SQL_DEBUG', True))
# -------------------------------
# SQUEST CONFIG
# -------------------------------
Expand Down
47 changes: 44 additions & 3 deletions Squest/utils/squest_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
from django.db.models import ProtectedError
from django.forms import Form
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy, NoReverseMatch, reverse
from django.utils.safestring import mark_safe
from django.views import View
from django.views.generic import CreateView, UpdateView, DeleteView, DetailView, FormView
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.detail import SingleObjectMixin, SingleObjectTemplateResponseMixin
from django.views.generic.edit import ModelFormMixin, ProcessFormView
from django_filters.views import FilterView
from django_tables2 import SingleTableMixin
from django_tables2.export import ExportMixin
Expand Down Expand Up @@ -39,7 +42,7 @@ def get_generic_url(self, action):
except AttributeError:
try:
return reverse(f'{self.django_content_type.app_label}:{self.django_content_type.model}_{action}',
kwargs=self.get_generic_url_kwargs())
kwargs=self.get_generic_url_kwargs())
except NoReverseMatch:
return '#'

Expand Down Expand Up @@ -140,6 +143,8 @@ def get_context_data(self, **kwargs):
return context




class SquestDeleteView(LoginRequiredMixin, SquestPermissionRequiredMixin, SquestView, DeleteView):
template_name = 'generics/confirm-delete-template.html'
context_object_name = "object"
Expand Down Expand Up @@ -215,7 +220,7 @@ def get_context_data(self, **kwargs):


class SquestFormView(LoginRequiredMixin, SquestPermissionRequiredMixin, SingleObjectMixin, SquestView, FormView):
template_name = 'generics/generic_form.html'
template_name = 'generics/confirm-delete-template.html'
context_object_name = "object"

def form_valid(self, form):
Expand Down Expand Up @@ -244,3 +249,39 @@ def get_context_data(self, **kwargs):
]
context['action'] = "edit"
return context


class SquestConfirmView(LoginRequiredMixin, SquestPermissionRequiredMixin, SingleObjectMixin, SquestView, FormView):
template_name = 'generics/confirm.html'
context_object_name = "object"

def form_valid(self, form):
return super().form_valid(form)

def get_success_url(self):
return self.get_object().get_absolute_url()
def get_permission_required(self):
pass

def get_context_data(self, **kwargs):
self.object = self.get_object()
context = super().get_context_data(**kwargs)
try:
object_url = self.object.get_absolute_url()
except AttributeError:
object_url = ""
context["bootstrap_type"] = "warning"
context['breadcrumbs'] = [
{
'text': self.django_content_type.name.capitalize(),
'url': self.get_generic_url('list')
},
{
'text': self.object,
'url': object_url
},

]

context['action'] = 'apply'
return context
5 changes: 2 additions & 3 deletions profiles/api/serializers/user_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ class Meta:
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'password', 'email', 'profile', 'first_name', 'last_name', 'is_staff',
'is_superuser', 'is_active', 'groups']
fields = ['id', 'username', 'password', 'email', 'first_name', 'last_name', 'is_staff',
'is_superuser', 'is_active']
read_only_fields = ['groups', ]

profile = ProfileSerializer(required=False)
password = CharField(
write_only=True,
required=True,
Expand Down
7 changes: 6 additions & 1 deletion service_catalog/api/views/request_api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ class RequestList(SquestListAPIView):
filterset_class = RequestFilter

def get_queryset(self):
return Request.get_queryset_for_user(self.request.user, 'service_catalog.view_request')
return Request.get_queryset_for_user(self.request.user, 'service_catalog.view_request').prefetch_related(
"user", "operation", "instance__requester", "instance__quota_scope", "instance__service",
"operation__service", "approval_workflow_state", "approval_workflow_state__approval_workflow",
"approval_workflow_state__current_step",
"approval_workflow_state__current_step__approval_step", "approval_workflow_state__approval_step_states"
)

def get_serializer_class(self):
if self.request.user.has_perm("service_catalog.view_admin_survey"):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.6 on 2023-11-28 11:26

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('service_catalog', '0036_approvalworkflow_enabled'),
]

operations = [
migrations.RemoveField(
model_name='approvalstep',
name='next',
),
migrations.AddField(
model_name='approvalworkflow',
name='version',
field=models.PositiveIntegerField(default=0, help_text='Current version'),
),
migrations.AddField(
model_name='approvalworkflowstate',
name='version',
field=models.PositiveIntegerField(default=1, help_text='ApprovalWorkflow version when instantiated'),
),
]
17 changes: 17 additions & 0 deletions service_catalog/migrations/0038_alter_request_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.6 on 2023-12-01 10:27

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('service_catalog', '0037_remove_approvalstep_next_approvalworkflow_version_and_more'),
]

operations = [
migrations.AlterModelOptions(
name='request',
options={'default_permissions': ('add', 'change', 'delete', 'view', 'list'), 'ordering': ['-last_updated'], 'permissions': [('accept_request', 'Can accept request'), ('cancel_request', 'Can cancel request'), ('reject_request', 'Can reject request'), ('reset_request', 'Can reset request'), ('archive_request', 'Can archive request'), ('unarchive_request', 'Can unarchive request'), ('re_submit_request', 'Can re-submit request'), ('process_request', 'Can process request'), ('need_info_request', 'Can ask info request'), ('view_admin_survey', 'Can view admin survey'), ('list_approvers', 'Can view who can accept')]},
),
]
35 changes: 23 additions & 12 deletions service_catalog/models/approval_step.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.db.models import ForeignKey, CharField, SET_NULL, CASCADE, IntegerField, ManyToManyField, PROTECT, TextField
from django.db.models.signals import post_save
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.urls import reverse

from Squest.utils.squest_model import SquestModel
Expand All @@ -22,15 +23,6 @@ class Meta(SquestModel.Meta):
name = CharField(max_length=100, blank=False)
position = IntegerField(null=True, blank=True, default=0)

next = ForeignKey(
"service_catalog.ApprovalStep",
blank=True,
null=True,
default=None,
on_delete=SET_NULL,
related_name="previous"
)

permission = ForeignKey(
Permission,
on_delete=PROTECT,
Expand Down Expand Up @@ -68,8 +60,7 @@ def on_create_set_position(cls, sender, instance, created, *args, **kwargs):
previous_steps = ApprovalStep.objects.filter(
approval_workflow=instance.approval_workflow).order_by('position').exclude(id=instance.id)
if previous_steps.exists():
instance.position = previous_steps.last().position + 1
instance.save()
ApprovalStep.objects.filter(id=instance.id).update(position=previous_steps.last().position + 1)

def save(self, *args, **kwargs):
# set the default permission
Expand All @@ -88,3 +79,23 @@ def who_can_approve(self, scope):


post_save.connect(ApprovalStep.on_create_set_position, sender=ApprovalStep)


@receiver(post_save, sender=ApprovalStep)
def increment_version(sender, instance, created, **kwargs):
from service_catalog.models import ApprovalWorkflow
ApprovalWorkflow.objects.filter(
id=instance.approval_workflow.id
).update(
version=instance.approval_workflow.version + 1
)


@receiver(post_delete, sender=ApprovalStep)
def increment_version_post_delete(sender, instance, *args, **kwargs):
from service_catalog.models import ApprovalWorkflow
ApprovalWorkflow.objects.filter(
id=instance.approval_workflow.id
).update(
version=instance.approval_workflow.version + 1
)
29 changes: 23 additions & 6 deletions service_catalog/models/approval_workflow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from django.db.models import CharField, ForeignKey, ManyToManyField, CASCADE, BooleanField
from django.db.models import CharField, ForeignKey, ManyToManyField, CASCADE, BooleanField, PositiveIntegerField
from django.db.models.signals import post_save
from django.dispatch import receiver

from Squest.utils.squest_model import SquestModel
from profiles.models import AbstractScope, GlobalScope, Scope
Expand All @@ -25,6 +27,8 @@ class ApprovalWorkflow(SquestModel):
help_text="This workflow will be triggered for the following scopes. Leave empty to trigger for all scopes"
)

version = PositiveIntegerField(help_text="Current version", default=0)

@property
def get_unused_fields(self):
all_fields = set(TowerSurveyField.objects.filter(operation=self.operation).values_list('name', flat=True))
Expand All @@ -33,6 +37,9 @@ def get_unused_fields(self):
flat=True))
return all_fields - used_fields




@property
def first_step(self):
first_step = ApprovalStep.objects.filter(approval_workflow=self, position=0)
Expand All @@ -46,13 +53,13 @@ def __str__(self):
def instantiate(self):
from service_catalog.models import ApprovalStepState
from service_catalog.models import ApprovalWorkflowState
new_approval_workflow_state = ApprovalWorkflowState.objects.create(approval_workflow=self)
new_approval_workflow_state = ApprovalWorkflowState.objects.create(approval_workflow=self, version=self.version)
for approval_step in self.approval_steps.all():
new_app_workflow_state = ApprovalStepState.objects.create(
current_step_state = ApprovalStepState.objects.create(
approval_workflow_state=new_approval_workflow_state,
approval_step=approval_step)
if approval_step.position == 0:
new_approval_workflow_state.current_step = new_app_workflow_state
new_approval_workflow_state.current_step = current_step_state
new_approval_workflow_state.save()
return new_approval_workflow_state

Expand All @@ -63,7 +70,7 @@ def _get_all_requests_that_should_use_workflow(self):
).exclude(
approval_workflows__id=self.id
)
expanded_scopes = self.scopes.expand().exclude(id__in=scopes_already_assigned_to_another_workflow.values("id") )
expanded_scopes = self.scopes.expand().exclude(id__in=scopes_already_assigned_to_another_workflow.values("id"))

if self.scopes.exists():
return Request.objects.filter(
Expand All @@ -82,8 +89,13 @@ def _get_all_requests_that_should_use_workflow(self):
def _get_request_using_workflow(self):
return Request.objects.filter(approval_workflow_state__approval_workflow=self, state=RequestState.SUBMITTED)

def _get_request_using_workflow_with_wrong_version(self):
return self._get_request_using_workflow().exclude(approval_workflow_state__version=self.version)
def _get_request_using_workflow_with_good_version(self):
return self._get_request_using_workflow().filter(approval_workflow_state__version=self.version)
def _get_request_to_reset(self):
return self._get_all_requests_that_should_use_workflow() | self._get_request_using_workflow()
# return self._get_all_requests_that_should_use_workflow() | self._get_request_using_workflow()
return self._get_all_requests_that_should_use_workflow().exclude(id__in=self._get_request_using_workflow_with_good_version().values_list("id",flat=True)) | self._get_request_using_workflow_with_wrong_version()

def reset_all_approval_workflow_state(self):
for request in self._get_request_to_reset().distinct():
Expand All @@ -98,3 +110,8 @@ def get_scopes(self):
return scopes
else:
return GlobalScope.load().get_scopes()


@receiver(post_save, sender=ApprovalWorkflow)
def increment_version(sender, instance, created, **kwargs):
ApprovalWorkflow.objects.filter(id=instance.id).update(version=instance.version + 1)
7 changes: 5 additions & 2 deletions service_catalog/models/approval_workflow_state.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import datetime

from django.contrib.auth.models import User
from django.db.models import ForeignKey, CASCADE
from django.db.models import ForeignKey, CASCADE, PositiveIntegerField

from Squest.utils.squest_model import SquestModel
from service_catalog.models import ApprovalState
Expand All @@ -26,11 +26,14 @@ class ApprovalWorkflowState(SquestModel):
related_query_name='current_approval_workflow_state'
)

version = PositiveIntegerField(help_text="ApprovalWorkflow version when instantiated", default=1)

def get_scopes(self):
return self.request.get_scopes()

def get_next_step(self):
next_step = self.approval_step_states.filter(approval_step__position=self.current_step.approval_step.position + 1)
next_step = self.approval_step_states.filter(
approval_step__position__gte=self.current_step.approval_step.position + 1)
if next_step.exists():
return next_step.first()
return None
Expand Down
16 changes: 11 additions & 5 deletions service_catalog/models/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Meta:
("accept_request", "Can accept request"),
("cancel_request", "Can cancel request"),
("reject_request", "Can reject request"),
("reset_request", "Can reset request"),
("archive_request", "Can archive request"),
("unarchive_request", "Can unarchive request"),
("re_submit_request", "Can re-submit request"),
Expand Down Expand Up @@ -162,11 +163,16 @@ def tower_job_url(self):
def need_info(self):
pass

@transition(field=state, source=[RequestState.NEED_INFO, RequestState.REJECTED], target=RequestState.SUBMITTED)
def re_submit(self):
if self.approval_workflow_state is not None:
self.approval_workflow_state.current_step.reset_to_pending()

@transition(field=state, source=[RequestState.NEED_INFO, RequestState.REJECTED, RequestState.SUBMITTED],
target=RequestState.SUBMITTED)
def re_submit(self, save=True):
if self.instance.state == InstanceState.ABORTED:
self.instance.state = InstanceState.PENDING
self.instance.save()
self.state = RequestState.SUBMITTED
self.setup_approval_workflow(save=True)
if save:
self.save()
@transition(field=state, source=[RequestState.SUBMITTED,
RequestState.NEED_INFO,
RequestState.REJECTED,
Expand Down
Loading

0 comments on commit 6a178cf

Please sign in to comment.