Skip to content

Commit

Permalink
Merge branch 'main' into feature/quickmessage-in-sector
Browse files Browse the repository at this point in the history
  • Loading branch information
AlanJaeger authored Jul 28, 2023
2 parents fc5546f + 5af7ad5 commit 2f1b29b
Show file tree
Hide file tree
Showing 33 changed files with 1,014 additions and 178 deletions.
5 changes: 5 additions & 0 deletions chats/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ("celery_app",)
10 changes: 10 additions & 0 deletions chats/apps/api/v1/accounts/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ def get_last_interaction(self, user: User):
return user.last_interaction


class UserSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
"first_name",
"last_name",
"email",
]


class UserNameSerializer(serializers.ModelSerializer):
class Meta:
model = User
Expand Down
11 changes: 10 additions & 1 deletion chats/apps/api/v1/contacts/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


class ContactSerializer(serializers.ModelSerializer):

room = serializers.SerializerMethodField()

class Meta:
Expand Down Expand Up @@ -87,3 +86,13 @@ class ContactWSSerializer(serializers.ModelSerializer):
class Meta:
model = Contact
fields = "__all__"


class ContactSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Contact
fields = [
"uuid",
"external_id",
"name",
]
116 changes: 77 additions & 39 deletions chats/apps/api/v1/dashboard/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
from rest_framework.response import Response

from chats.apps.api.v1.dashboard.presenter import (
get_agents_data,
get_export_data,
get_general_data,
get_sector_data,
)
from chats.apps.api.v1.dashboard.serializers import (
DashboardRawDataSerializer,
Expand All @@ -19,6 +16,10 @@
from chats.apps.api.v1.permissions import HasDashboardAccess
from chats.apps.projects.models import Project

import io
from chats.core.excel_storage import ExcelStorage
import json


class DashboardLiveViewset(viewsets.GenericViewSet):
lookup_field = "uuid"
Expand Down Expand Up @@ -106,35 +107,54 @@ def export(self, request, *args, **kwargs):
project = self.get_object()
filter = request.query_params
dataset = get_export_data(project, filter)
data_frame_rooms = pandas.DataFrame(dataset)

filename = "dashboard_export_data"
filename = "dashboard_rooms_export_data"

if "xls" in filter:
response = HttpResponse(
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)
response["Content-Disposition"] = (
'attachment; filename="' + filename + ".xls"
excel_rooms_buffer = io.BytesIO()
with pandas.ExcelWriter(excel_rooms_buffer, engine="xlsxwriter") as writer:
data_frame_rooms.to_excel(
writer,
sheet_name="rooms_infos",
startrow=1,
startcol=0,
index=False,
)
excel_rooms_buffer.seek(0) # Move o cursor para o início do buffer
storage = ExcelStorage()

bytes_archive = excel_rooms_buffer.getvalue()

with storage.open(filename + ".xlsx", "wb") as up_file:
up_file.write(bytes_archive)
file_url = storage.url(up_file.name)

data = {"path_file": file_url}

return HttpResponse(
json.dumps(data),
content_type="application/javascript; charset=utf8",
)
else:
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = (
'attachment; filename="' + filename + ".csv"
)

table = pandas.DataFrame(dataset)
table.rename(
columns={
0: "Queue Name",
1: "Waiting Time",
2: "Response Time",
3: "Interaction Time",
4: "Open",
},
inplace=True,
)
table.to_csv(response, encoding="utf-8", index=False)
return response
table = pandas.DataFrame(dataset)
table.rename(
columns={
0: "Queue Name",
1: "Waiting Time",
2: "Response Time",
3: "Interaction Time",
4: "Open",
},
inplace=True,
)
table.to_csv(response, encoding="utf-8", index=False)
return response

@action(
detail=True,
Expand All @@ -149,24 +169,28 @@ def export_dashboard(self, request, *args, **kwargs):
project = self.get_object()
filter = request.query_params

general_dataset = get_general_data(project, filter)
userinfo_dataset = get_agents_data(project, filter)
sector_dataset = get_sector_data(project, filter)
user_info_context = {}
user_info_context["filters"] = request.query_params

# General data
general_dataset = dashboard_general_data(context=filter, project=project)
raw_dataset = DashboardRawDataSerializer(instance=project, context=filter)
combined_dataset = {**general_dataset, **raw_dataset.data}

# Agents Data
userinfo_dataset = dashboard_agents_data(context=filter, project=project)
# # Sectors Data
sector_dataset = dashboard_division_data(context=filter, project=project)

filename = "dashboard_export_data"

data_frame = pandas.DataFrame([general_dataset])
data_frame_1 = pandas.read_json(userinfo_dataset)
data_frame_2 = pandas.read_json(sector_dataset)
data_frame = pandas.DataFrame([combined_dataset])
data_frame_1 = pandas.DataFrame(userinfo_dataset)
data_frame_2 = pandas.DataFrame(sector_dataset)

if "xls" in filter:
response = HttpResponse(
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)
response["Content-Disposition"] = (
'attachment; filename="' + filename + ".xlsx"
)
with pandas.ExcelWriter(response, engine="xlsxwriter") as writer:
excel_buffer = io.BytesIO()
with pandas.ExcelWriter(excel_buffer, engine="xlsxwriter") as writer:
data_frame.to_excel(
writer,
sheet_name="dashboard_infos",
Expand All @@ -188,16 +212,30 @@ def export_dashboard(self, request, *args, **kwargs):
startcol=0,
index=False,
)
return response
excel_buffer.seek(0) # Move o cursor para o início do buffer
storage = ExcelStorage()

bytes_archive = excel_buffer.getvalue()

with storage.open(filename + ".xlsx", "wb") as up_file:
up_file.write(bytes_archive)
file_url = storage.url(up_file.name)

data = {"path_file": file_url}

return HttpResponse(
json.dumps(data),
content_type="application/javascript; charset=utf8",
)

else:
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = (
'attachment; filename="' + filename + ".csv"
)

data_frame.to_csv(response, index=False)
data_frame_1.to_csv(response, index=False, mode="a")
data_frame_2.to_csv(response, index=False, mode="a")
data_frame.to_csv(response, index=False, sep=";")
data_frame_1.to_csv(response, index=False, mode="a", sep=";")
data_frame_2.to_csv(response, index=False, mode="a", sep=";")

return response
5 changes: 2 additions & 3 deletions chats/apps/api/v1/external/msgs/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,14 @@ def create(self, validated_data):
if direction == "incoming":
validated_data["contact"] = room.contact

is_waiting = room.get_is_waiting()
was_24h_valid = room.is_24h_valid
was_24h_valid = room.validate_24h
msg = super().create(validated_data)
media_list = [MessageMedia(**media_data, message=msg) for media_data in medias]
medias = MessageMedia.objects.bulk_create(media_list)

if direction == "incoming":
validated_data["contact"] = room.contact
if is_waiting:
if room.is_waiting:
room.is_waiting = False
room.save()
room.notify_room("update")
Expand Down
79 changes: 73 additions & 6 deletions chats/apps/api/v1/external/permissions.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,82 @@
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import permissions

from chats.apps.projects.models import ProjectPermission


class IsAdminPermission(permissions.BasePermission):
def has_permission(self, request, view): # pragma: no cover
auth_header = request.META.get("HTTP_AUTHORIZATION")
if view.action in ["list", "create"]:
try:
permission = request.auth
project = permission.project

validation = ValidatePermissionRequest(
request_data=request.data or request.query_params, project=project
)

return validation.is_valid
except (AttributeError, IndexError, ProjectPermission.DoesNotExist):
return False

return super().has_permission(request, view)

def has_object_permission(self, request, view, obj):
"""
Return `True` if permission is granted, `False` otherwise.
"""
try:
permission = request.auth
project = obj.project
except ProjectPermission.DoesNotExist:
return False
return permission.project == project


LEVEL_NAME_MAPPING = {
"project_uuid": "project",
"project": "project",
"sector_uuid": "pk",
"sector": "pk",
"queue_uuid": "queues",
"queue": "queues",
"room__uuid": "queues__rooms",
"room": "queues__rooms",
}


class ValidatePermissionRequest:
def __init__(self, request_data, project) -> None:
self.project = project
self.data = request_data
self.queryset = {}

# Get the instance type and pk
for key in [
"project",
"sector",
"queue",
"project_uuid",
"sector_uuid",
"queue_uuid",
"room__uuid",
"room",
]:
self.level_name = LEVEL_NAME_MAPPING[key]
self.level_id = self.data.get(key, None)
if self.level_name != "project" and self.level_id is not None:
self.queryset = {self.level_name: self.level_id}
break
elif self.level_name == "project" and self.level_id is not None:
break

@property
def is_valid(self):
try:
auth_token = auth_header.split()[1]
return (
True if ProjectPermission.objects.get(pk=auth_token, role=1) else False
)
except (AttributeError, IndexError, ProjectPermission.DoesNotExist):
if self.level_name == "project":
return str(self.project.pk) == self.level_id
if self.queryset != {}:
return self.project.sectors.filter(**self.queryset).exists()
except ObjectDoesNotExist:
return False
return False
6 changes: 5 additions & 1 deletion chats/apps/api/v1/external/rooms/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def create(self, validated_data):
contact, queue, user, groups, created, flow_uuid, project
)

room = Room.objects.create(**validated_data, contact=contact, queue=queue)
room = Room.objects.create(**validated_data, contact=contact, queue=queue, is_waiting=True)
RoomMetrics.objects.create(room=room)

return room
Expand Down Expand Up @@ -188,6 +188,10 @@ def check_work_time(self, sector, created_on):
raise ValidationError(
{"detail": _("Contact cannot be done outside working hours")}
)
elif sector.validate_agent_status() is False:
raise ValidationError(
{"detail": _("Contact cannot be done when agents are offline")}
)

def handle_urn(self, validated_data):
is_anon = validated_data.pop("is_anon", False)
Expand Down
Loading

0 comments on commit 2f1b29b

Please sign in to comment.