Skip to content

Commit

Permalink
Global UI/UX patch and fixes for the open source free plan
Browse files Browse the repository at this point in the history
- Improvements in displaying attached files in task descriptions
- Сorrect display of long words in the name of a subprocess
- Template editor: Fix template owners and "share" logiс for the free plan. Fixed display of conditions with date field.
- Fix custom dates filter for highlights page
- Fixes for displaying variables in text: Added ellipsis for long variable names, etc.
- Guest UI fix: task events now displayed correctly, task due date widget removed for guest
- Support time zones for due dates
  • Loading branch information
pneumojoseph committed Dec 11, 2024
1 parent 88f393f commit 1df2da7
Show file tree
Hide file tree
Showing 125 changed files with 2,356 additions and 987 deletions.
34 changes: 33 additions & 1 deletion core/pneumatic_backend/accounts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,38 @@ def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
return field


class ContactInline(StackedInline):

model = Contact
extra = 0
verbose_name = 'Contact'
verbose_name_plural = 'Contacts'
show_change_link = True
fields = (
'email',
'first_name',
'last_name',
'photo',
'job_title',
'source',
'source_id',
'status',
)
readonly_fields = (
'email',
'first_name',
'last_name',
'photo',
'job_title',
'source',
'source_id',
'status',
)

def has_add_permission(self, request):
return False


class UsersAdmin(UserAdmin, SignUpMixin):

add_fieldsets = (
Expand All @@ -151,7 +183,7 @@ class UsersAdmin(UserAdmin, SignUpMixin):
),
}),
)
inlines = [GroupInline]
inlines = [GroupInline, ContactInline]
fieldsets = (
(
messages.MSG_A_0020,
Expand Down
12 changes: 11 additions & 1 deletion core/pneumatic_backend/accounts/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,17 @@ class NotificationFilter(FilterSet):

class Meta:
model = Notification
fields = ('status', )
fields = (
'status',
'ordering',
)

ordering = DefaultOrderingFilter(
fields=(
('datetime', 'datetime'),
),
default=('-datetime',)
)


class UsersListFilterSet(FilterSet):
Expand Down
4 changes: 2 additions & 2 deletions core/pneumatic_backend/accounts/serializers/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ class Meta:

def validate_logo_lg(self, value):
if value:
if not self.instance.is_subscribed or self.instance.is_tenant:
if self.instance.is_tenant:
raise ValidationError(MSG_A_0003)
return value

def validate_logo_sm(self, value):
if value:
if not self.instance.is_subscribed or self.instance.is_tenant:
if self.instance.is_tenant:
raise ValidationError(MSG_A_0003)
return value

Expand Down
8 changes: 3 additions & 5 deletions core/pneumatic_backend/accounts/serializers/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ def get_task(self, instance):
result['delay'] = DelaySerializer(instance=delay).data
elif instance.type == NotificationType.DUE_DATE_CHANGED:
if instance.task.due_date:
result['due_date'] = instance.task.due_date.strftime(
datetime_format
)
result['due_date_tsp'] = instance.task.due_date.timestamp()
else:
result['due_date'] = None
return result
result['due_date_tsp'] = None
return result
38 changes: 26 additions & 12 deletions core/pneumatic_backend/accounts/tests/views/accounts/test_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def test_partial_update__free_plan_name__ok(
assert account.name == new_name


def test_partial_update__free_plan_change_logo_lg__validation_error(
def test_partial_update__free_plan_change_logo_lg__ok(
api_client,
):

Expand All @@ -72,14 +72,21 @@ def test_partial_update__free_plan_change_logo_lg__validation_error(
}
)

assert response.status_code == 400
assert response.data['code'] == ErrorCode.VALIDATION_ERROR
assert response.data['details']['name'] == 'logo_lg'
assert response.data['message'] == MSG_A_0003
assert response.data['details']['reason'] == MSG_A_0003
# assert
assert response.status_code == 200
assert response.data['id'] == account.id
assert response.data['name'] == account.name
assert response.data['date_joined'] is not None
assert response.data['plan_expiration'] is None
assert response.data['lease_level'] == LeaseLevel.STANDARD
assert response.data['logo_lg'] == logo_lg
assert response.data['logo_sm'] is None

account.refresh_from_db()
assert account.logo_lg == logo_lg


def test_partial_update__free_plan_change_logo_sm__validation_error(
def test_partial_update__free_plan_change_logo_sm__ok(
api_client,
):
# arrange
Expand All @@ -96,11 +103,18 @@ def test_partial_update__free_plan_change_logo_sm__validation_error(
}
)

assert response.status_code == 400
assert response.data['code'] == ErrorCode.VALIDATION_ERROR
assert response.data['details']['name'] == 'logo_sm'
assert response.data['message'] == MSG_A_0003
assert response.data['details']['reason'] == MSG_A_0003
# assert
assert response.status_code == 200
assert response.data['id'] == account.id
assert response.data['name'] == account.name
assert response.data['date_joined'] is not None
assert response.data['plan_expiration'] is None
assert response.data['lease_level'] == LeaseLevel.STANDARD
assert response.data['logo_lg'] is None
assert response.data['logo_sm'] == logo_sm

account.refresh_from_db()
assert account.logo_sm == logo_sm


def test_partial_update__tenant__name__ok(
Expand Down
3 changes: 1 addition & 2 deletions core/pneumatic_backend/accounts/tests/views/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,7 @@ def test_list__type_due_date_changed__ok(self, api_client):
assert data['status'] == NotificationStatus.NEW
assert data['task']['id'] == task.id
assert data['task']['name'] == task.name
str_due_date = task.due_date.strftime(date_format)
assert data['task']['due_date'] == str_due_date
assert data['task']['due_date_tsp'] == task.due_date.timestamp()
assert data['workflow']['id'] == workflow.id
assert data['workflow']['name'] == workflow.name

Expand Down
5 changes: 1 addition & 4 deletions core/pneumatic_backend/accounts/views/notifications.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from typing import List
from django.contrib.auth import get_user_model
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.generics import (
CreateAPIView,
Expand Down Expand Up @@ -46,9 +45,7 @@ class NotificationsViewSet(
pagination_class = LimitOffsetPagination
serializer_class = NotificationsSerializer
filterset_class = NotificationFilter
filter_backends = [OrderingFilter, PneumaticFilterBackend]
ordering_fields = ['datetime']
ordering = ['-datetime']
filter_backends = [PneumaticFilterBackend]

def get_permissions(self):
if self.action in 'destroy':
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import BaseCommand
from django.db import transaction
from pneumatic_backend.accounts.services import (
AccountService,
UserService,
)
from pneumatic_backend.reports.serializers import UserModel


class Command(BaseCommand):
Expand All @@ -12,11 +13,18 @@ class Command(BaseCommand):

def add_arguments(self, parser):
parser.add_argument("email", type=str)
parser.add_argument("password", type=str)

def handle(self, *args, **options):
email = options.get("email")
if not email:
raise CommandError('Parameter email is required')
email = options["email"]
password = options["password"]

if UserModel.objects.filter(email=email).exists():
self.stdout.write(
self.style.ERROR('User with the given email already exists.')
)
return

account_service = AccountService()
user_service = UserService()
with transaction.atomic():
Expand All @@ -27,9 +35,12 @@ def handle(self, *args, **options):
account=account,
email=email,
first_name='Admin',
raw_password='password',
raw_password=password,
is_account_owner=True,
)
account_owner.is_superuser = True
account_owner.is_staff = True
account_owner.save()
self.stdout.write(
self.style.SUCCESS('User successfully created.')
)
2 changes: 1 addition & 1 deletion core/pneumatic_backend/authentication/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def process_response(self, request, response):
method=request.method,
title='API request',
path=request.path,
body=body,
request_data=body,
http_status=response.status_code,
response_data=response_data
)
Expand Down
71 changes: 50 additions & 21 deletions core/pneumatic_backend/authentication/services/microsoft.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pneumatic_backend.authentication.models import AccessToken
from pneumatic_backend.generics.mixins.services import CacheMixin
from pneumatic_backend.storage.google_cloud import GoogleCloudService
from pneumatic_backend.logs.service import AccountLogService
from pneumatic_backend.authentication.services import exceptions
from pneumatic_backend.utils.logging import (
capture_sentry_message,
Expand Down Expand Up @@ -440,26 +441,54 @@ def update_user_contacts(self, user: UserModel):

""" Save all organization users in contacts """

access_token = self._get_access_token(user.id)
users_data = self._get_users(access_token)
for user_profile in users_data['value']:
email = self._get_user_profile_email(user_profile)
if email and email != user.email:
photo = self._get_user_photo(
access_token=access_token,
user_id=user_profile['id']
)
first_name = user_profile['givenName'] or email.split('@')[0]
Contact.objects.update_or_create(
account=user.account,
response_data = {'created_contacts': [], 'updated_contacts': []}
path = f'{self.api_url}{self.users_path}'
title = f'Contacts request: {user.email}'
http_status = 200
try:
access_token = self._get_access_token(user.id)
users_data = self._get_users(access_token)
response_data['users_data'] = users_data
for user_profile in users_data['value']:
email = self._get_user_profile_email(user_profile)
if email and email != user.email:
photo = self._get_user_photo(
access_token=access_token,
user_id=user_profile['id']
)
first_name = (
user_profile['givenName'] or email.split('@')[0]
)
_, created = Contact.objects.update_or_create(
account=user.account,
user=user,
source=SourceType.MICROSOFT,
email=email,
defaults={
'photo': photo,
'first_name': first_name,
'last_name': user_profile['surname'],
'job_title': user_profile['jobTitle'],
'source_id': user_profile['id'],
}
)
if created:
response_data['created_contacts'].append(email)
else:
response_data['updated_contacts'].append(email)

except Exception as ex:
http_status = 400
response_data['message'] = str(ex)
response_data['exception_type'] = type(ex)
response_data['details'] = getattr(ex, 'details')
finally:
if user.account.log_api_requests:
AccountLogService().contacts_request(
user=user,
source=SourceType.MICROSOFT,
email=email,
defaults={
'photo': photo,
'first_name': first_name,
'last_name': user_profile['surname'],
'job_title': user_profile['jobTitle'],
'source_id': user_profile['id'],
}
path=path,
title=title,
http_status=http_status,
response_data=response_data,
contractor='Microsoft Graph API',
)
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def test__get_request_with_data__ok(api_client, mocker):
status=AccountEventStatus.SUCCESS,
http_status=200,
direction=RequestDirection.RECEIVED,
body=params,
request_data=params,
)


Expand Down Expand Up @@ -153,7 +153,7 @@ def test__get_request_with_query_string__ok(api_client, mocker):
status=AccountEventStatus.SUCCESS,
http_status=200,
direction=RequestDirection.RECEIVED,
body={'key_1': 'Value1,Value2', 'key_2': '123'},
request_data={'key_1': 'Value1,Value2', 'key_2': '123'},
)


Expand Down Expand Up @@ -194,8 +194,8 @@ def test__post_request_with_data__ok(api_client, mocker):
direction=RequestDirection.RECEIVED,
http_status=204,
)
assert event.body == data
assert event.error is None
assert event.request_data == data
assert event.response_data is None


def test__head_request__skip(api_client, mocker):
Expand Down Expand Up @@ -311,8 +311,10 @@ def test__bad_request__save_error(api_client, mocker):
direction=RequestDirection.RECEIVED,
http_status=400,
)
assert event.body == data
assert event.error['code'] == 'validation_error'
assert event.error['message'] == 'This field is required.'
assert event.error['details']['name'] == 'name'
assert event.error['details']['reason'] == 'This field is required.'
assert event.request_data == data
assert event.response_data['code'] == 'validation_error'
assert event.response_data['message'] == 'This field is required.'
assert event.response_data['details']['name'] == 'name'
assert event.response_data['details']['reason'] == (
'This field is required.'
)
Loading

0 comments on commit 1df2da7

Please sign in to comment.