Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3eb0df3
#86bywpjq6 - CacheLock izboljšave
May 28, 2024
5801d98
#86byyqe9f - Sistematično profiliranje celery taskov
May 30, 2024
2890755
#86byyqe9f - Sistematično profiliranje celery taskov
May 30, 2024
a1cb4f4
Merge branch 'refs/heads/#86bywpjq6-CacheLock-izboljšave' into #86byy…
Jun 7, 2024
a18e085
#86bywpjq6 - CacheLock izboljšave
Jun 7, 2024
6b66a72
#86byyqe9f - Sistematično profiliranje celery taskov
May 30, 2024
d018f34
#86byyqe9f - Sistematično profiliranje celery taskov
May 30, 2024
c879fde
#86bywpjq6 - CacheLock izboljšave
May 28, 2024
690a87e
#86bywpjq6 - CacheLock izboljšave
Jun 7, 2024
31899e8
Merge remote-tracking branch 'origin/#86byyqe0k-Sistematično-profilir…
Jun 10, 2024
009cac8
Failing tests
Jun 11, 2024
037d2bd
Failing tests
Jun 11, 2024
30d2ae6
Failing tests
Jun 11, 2024
ed4530b
Version string...
Jun 14, 2024
62a89be
#86byyqe9f - Sistematično profiliranje celery taskov
May 30, 2024
4ea83e2
#86byyqe9f - Sistematično profiliranje celery taskov
May 30, 2024
7ed5b67
Failing tests
Jun 11, 2024
a8dbad3
Failing tests
Jun 11, 2024
830418b
Failing tests
Jun 11, 2024
3954c2e
Version string...
Jun 14, 2024
61bbfd3
#86bz85z0d - NEW DF - Failing tests in github actions
Jun 19, 2024
a752fb8
Merge remote-tracking branch 'origin/#86byyqe0k-Sistematično-profilir…
Jun 19, 2024
a33f177
#86bz85z0d - NEW DF - Failing tests in github actions
Jun 20, 2024
48b7416
#86bz85z0d - NEW DF - Failing tests in github actions
Jun 20, 2024
8df6f60
#86bz85x5v - NEW DF - Swagger is not working
Jun 21, 2024
8768d04
#86bz85ycf - NEW DF - Incomplete security policies
Jun 21, 2024
ba4b086
#86bzqq2pv - Allow for no currently-selected project: refactor select…
Aug 1, 2024
88d9f49
#86bzqq3xd - Allow for no currently-selected project: settings.py var…
Aug 14, 2024
5a0672f
Merge branch 'main' into #86byyqe0k-Sistematično-profiliranje-managem…
Brontes Aug 19, 2024
7a51fa9
Merge branch '#86byyqe0k-Sistematično-profiliranje-management-komand_…
Brontes Aug 19, 2024
37d6f30
Merge branch 'main' into #86bzqq2pv-Allow-for-no-currently-selected-p…
Brontes Nov 26, 2024
a83696d
Merge branch '#86bzqq2pv-Allow-for-no-currently-selected-project-refa…
Brontes Nov 26, 2024
f3bb724
Failing tests
Brontes Nov 26, 2024
43dedc8
Failing tests
Brontes Nov 26, 2024
57e85e2
Merge branch '#86bzqq2pv-Allow-for-no-currently-selected-project-refa…
Brontes Nov 26, 2024
be06512
Failing tests
Brontes Nov 26, 2024
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
61 changes: 0 additions & 61 deletions django_project_base/account/middleware.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,9 @@
import json

import swapper

from django.conf import settings
from django.contrib.sessions.middleware import SessionMiddleware as SessionMiddlewareBase
from django.utils.functional import SimpleLazyObject
from rest_framework.authentication import get_authorization_header


class ProjectNotSelectedError(NotImplementedError):
def __init__(self, message: str, *args: object) -> None:
super().__init__(message, *args)
self.message = message


def selected_project_not_setup():
raise ProjectNotSelectedError(
"""The functionality called requires settings variables that establish currently selected project.
Check docs for DJANGO_PROJECT_BASE_BASE_REQUEST_URL_VARIABLES and its "project" setting.
The setting needs to be declared and currently selected project passed at least once from the front-end
"""
)


def load_selected_project(slug: str):
def load():
ProjectModel = swapper.load_model("django_project_base", "Project")
try:
return ProjectModel.objects.prefetch_related("owner").get(slug=slug)
except ProjectModel.DoesNotExist:
selected_project_not_setup()

return load


class SessionMiddleware(SessionMiddlewareBase):
def get_request_params(self, request):
if request.method in ["POST", "PUT", "PATCH", "DELETE"]:
body_data = json.loads(request.body.decode("utf-8"))
if isinstance(body_data, list):
return body_data[0]
return body_data
else:
return getattr(request, request.method)

def process_request(self, request):
auth = get_authorization_header(request).split()
if auth and auth[0].lower() == b"sessionid":
Expand All @@ -54,27 +14,6 @@ def process_request(self, request):

request.session = self.SessionStore(session_key)

# determine if currently selected project has been passed with the request and set it to session if so
# this requires UrlVarsMiddleware to have been installed before this middleware
current_project_attr = (
getattr(settings, "DJANGO_PROJECT_BASE_BASE_REQUEST_URL_VARIABLES", {})
.get("project", {})
.get("value_name", None)
)

# also set request.selected_project variables or throw errors on access if conditions not satisfied
if current_project_attr:
if curr_project_slug := getattr(request, current_project_attr, None):
request.session[current_project_attr] = curr_project_slug
else:
curr_project_slug = request.session.get(current_project_attr, None)

request.selected_project_slug = curr_project_slug
request.selected_project = SimpleLazyObject(load_selected_project(curr_project_slug))
else:
request.selected_project_slug = SimpleLazyObject(selected_project_not_setup)
request.selected_project = SimpleLazyObject(selected_project_not_setup)

def process_response(self, request, response):
if getattr(response, "returntype", None) == "json":
process_response = super().process_response(request, response)
Expand Down
10 changes: 6 additions & 4 deletions django_project_base/account/rest/invite.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

from django_project_base.base.event import UserInviteFoundEvent
from django_project_base.base.exceptions import InviteActionNotImplementedException
from django_project_base.base.fields import ProjectField
from django_project_base.base.mixins import ProjectMixin
from django_project_base.base.viewsets import ProjectFilteringViewSet
from django_project_base.constants import INVITE_NOTIFICATION_TEXT
from django_project_base.notifications.email_notification import EMailNotificationWithListOfEmails
Expand Down Expand Up @@ -45,7 +47,7 @@ def __init__(self, *args, **kw):
self.allow_blank = True


class ProjectUserInviteSerializer(ModelSerializer):
class ProjectUserInviteSerializer(ProjectMixin, ModelSerializer):
template_context = dict(url_reverse="project-user-invite")
form_titles = {
"table": _("Project invites"),
Expand All @@ -62,9 +64,9 @@ class ProjectUserInviteSerializer(ModelSerializer):

id = fields.AutoGeneratedField(display=DisplayMode.HIDDEN)
invited_by = fields.AutoGeneratedField(display=DisplayMode.HIDDEN)
project = fields.PrimaryKeyRelatedField(
display=DisplayMode.SUPPRESS,
queryset=swapper.load_model("django_project_base", "Project").objects.all(),
# Project field is hidden... Because when you invite member to project, project id is always known.
project = ProjectField(
display=DisplayMode.HIDDEN, queryset=swapper.load_model("django_project_base", "Project").objects.none()
)
role = fields.PrimaryKeyRelatedField(
queryset=swapper.load_model("django_project_base", "Role").objects.all(),
Expand Down
12 changes: 8 additions & 4 deletions django_project_base/account/rest/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
from rest_registration.exceptions import UserNotFound

from django_project_base.account.constants import MERGE_USERS_QS_CK
from django_project_base.account.middleware import ProjectNotSelectedError
from django_project_base.account.rest.project_profiles_utils import get_project_members
from django_project_base.base.event import UserRegisteredEvent
from django_project_base.base.permissions import IsProjectOwner
Expand All @@ -48,7 +47,12 @@
)
from django_project_base.notifications.models import DjangoProjectBaseMessage
from django_project_base.permissions import BasePermissions
from django_project_base.rest.project import ProjectSerializer, ProjectViewSet
from django_project_base.project_selection import (
get_project_slug_from_session,
get_user_projects,
ProjectNotSelectedError,
)
from django_project_base.rest.project import ProjectSerializer
from django_project_base.settings import DELETE_PROFILE_TIMEDELTA, USER_CACHE_KEY
from django_project_base.utils import get_pk_name

Expand Down Expand Up @@ -460,7 +464,7 @@ def get_current_profile(self, request: Request, **kwargs) -> Response:
response_data["default_project"] = ProjectSerializer(request.selected_project).data
cache.set(cache_key, request.selected_project.pk, timeout=None)
except ProjectNotSelectedError:
q = ProjectViewSet._get_queryset_for_request(request)
q = get_user_projects(self.request.user)
# todo this might be a problem if the cache is cleared. then selected project might change for some users as
# previously selected project would be forgotten. might have to move this to a table?
previously_selected_project_pk = cache.get(cache_key)
Expand Down Expand Up @@ -638,7 +642,7 @@ def reset_user_data(self, request: Request, **kwargs) -> Response:
def create(self, request, *args, **kwargs):
response = super().create(request, *args, **kwargs)
project = swapper.load_model("django_project_base", "Project").objects.get(
slug=getattr(self.request, settings.DJANGO_PROJECT_BASE_BASE_REQUEST_URL_VARIABLES["project"]["value_name"])
slug=get_project_slug_from_session(self.request)
)
if (
sett := swapper.load_model("django_project_base", "ProjectSettings")
Expand Down
2 changes: 1 addition & 1 deletion django_project_base/account/rest/project_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
from rest_framework.utils import model_meta

from django_project_base.account.rest.profile import ProfileSerializer, ProfileViewSet
from django_project_base.project_selection import ProjectNotSelectedError

from ...base.permissions import is_project_owner, is_superuser, IsProjectOwnerOrMemberReadOnly, project_is_selected
from ..middleware import ProjectNotSelectedError
from .project_profiles_utils import filter_project_members_fields, get_project_members


Expand Down
2 changes: 1 addition & 1 deletion django_project_base/account/rest/project_profiles_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from dynamicforms import fields
from rest_framework.request import Request

from django_project_base.account.middleware import ProjectNotSelectedError
from django_project_base.project_selection import ProjectNotSelectedError


def get_project_members(request: Request, project=None) -> QuerySet:
Expand Down
7 changes: 2 additions & 5 deletions django_project_base/base/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from django_project_base.constants import EMAIL_SENDER_ID_SETTING_NAME, SMS_SENDER_ID_SETTING_NAME
from django_project_base.notifications.email_notification import SystemEMailNotificationWithListOfEmails
from django_project_base.notifications.models import DjangoProjectBaseMessage
from django_project_base.project_selection import get_current_project_attr


class UserModel:
Expand Down Expand Up @@ -105,11 +106,7 @@ def trigger(self, payload=None, **kwargs):
payload.accepted = datetime.datetime.now()
payload.save(update_fields=["accepted"])

if current_project_attr := (
getattr(settings, "DJANGO_PROJECT_BASE_BASE_REQUEST_URL_VARIABLES", {})
.get("project", {})
.get("value_name", None)
):
if current_project_attr := get_current_project_attr():
kwargs["request"].session[current_project_attr] = payload.project.slug
kwargs["request"].session.pop("invite-pk", None)

Expand Down
28 changes: 28 additions & 0 deletions django_project_base/base/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,31 @@ def display_value(self, instance):
return ""

return str(instance)


class ProjectField(df_fields.PrimaryKeyRelatedField):
# This class is used for fields that represents project fields.
# There is serializer mixin ProjectMixin that shows or hides this field on form/table based on setting
# DJANGO_PROJECT_BASE_SELECTED_PROJECT_MODE

_attr_display_default = False
_attr_queryset_default = False

def __init__(self, **kw):
from dynamicforms.mixins import DisplayMode

from django_project_base.base.models import get_project_model

if "display" not in kw:
kw["display"] = DisplayMode.HIDDEN
self._attr_display_default = True
if "queryset" not in kw:
kw["queryset"] = get_project_model().objects.none()
self._attr_queryset_default = True

super().__init__(**kw)

def set_field_attrs(self, **kw):
for k, v in kw.items():
if getattr(self, f"_attr_{k}_default", True):
setattr(self, k, v)
2 changes: 1 addition & 1 deletion django_project_base/base/filter_to_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
def filter_queryset_to_project(
queryset: QuerySet, project_field: str = "project", project: "BaseProject" = None
) -> QuerySet:
from django_project_base.account.middleware import ProjectNotSelectedError
from django_project_base.base.middleware import get_current_request, has_current_request
from django_project_base.project_selection import ProjectNotSelectedError

if not project and has_current_request():
project = getattr(get_current_request(), "selected_project", None)
Expand Down
35 changes: 22 additions & 13 deletions django_project_base/base/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,23 @@ def get_parameter(request, value_name: str, url_part: str) -> Optional[object]:
if value_from_header is not None and value_from_header not in ("", "null"):
return value_from_header

path_parts = request.path_info.split("/")
if isinstance(url_part, (list, tuple)) and isinstance(url_part[0], int) and isinstance(url_part[1], (list, tuple)):
parm = path_parts[url_part[0]] if len(path_parts) > url_part[0] else None
return parm if parm not in url_part[1] else None

try:
project_info = next(iter(filter(lambda f: f and url_part in f, path_parts)))
except StopIteration:
project_info = None
if project_info:
url_part_len = len(url_part)
return project_info[url_part_len:]
if url_part:
path_parts = request.path_info.split("/")
if (
isinstance(url_part, (list, tuple))
and isinstance(url_part[0], int)
and isinstance(url_part[1], (list, tuple))
):
parm = path_parts[url_part[0]] if len(path_parts) > url_part[0] else None
return parm if parm not in url_part[1] else None

try:
project_info = next(iter(filter(lambda f: f and url_part in f, path_parts)))
except StopIteration:
project_info = None
if project_info:
url_part_len = len(url_part)
return project_info[url_part_len:]

return None

Expand All @@ -42,8 +47,12 @@ def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
from django_project_base.constants import BASE_REQUEST_URL_VARIABLES_PROJECT_KEY

for value, config in settings.DJANGO_PROJECT_BASE_BASE_REQUEST_URL_VARIABLES.items():
if param := get_parameter(request, value, config["url_part"]):
if value == BASE_REQUEST_URL_VARIABLES_PROJECT_KEY:
continue
if param := get_parameter(request, value, config.get("url_part", "")):
setattr(request, config["value_name"], param)

_threadmap[threading.get_ident()] = request
Expand Down
Loading
Loading