Merge branch 'develop' into feature/dockerbuild
This commit is contained in:
commit
b4c0ab0d67
@ -132,6 +132,7 @@ extensions = {
|
|||||||
'sphinx.ext.autodoc',
|
'sphinx.ext.autodoc',
|
||||||
'sphinx.ext.intersphinx',
|
'sphinx.ext.intersphinx',
|
||||||
'sphinx.ext.viewcode',
|
'sphinx.ext.viewcode',
|
||||||
|
'sphinx.ext.napoleon',
|
||||||
'sphinx_rtd_theme',
|
'sphinx_rtd_theme',
|
||||||
'sphinx.ext.graphviz',
|
'sphinx.ext.graphviz',
|
||||||
'sphinx.ext.inheritance_diagram',
|
'sphinx.ext.inheritance_diagram',
|
||||||
@ -205,3 +206,5 @@ set_type_checking_flag = True
|
|||||||
typehints_fully_qualified = True
|
typehints_fully_qualified = True
|
||||||
always_document_param_types = True
|
always_document_param_types = True
|
||||||
typehints_document_rtype = True
|
typehints_document_rtype = True
|
||||||
|
|
||||||
|
napoleon_attr_annotations = True
|
||||||
|
@ -115,7 +115,7 @@ class ZendeskAdmin:
|
|||||||
user = self.admin.users.search(email).values[0]
|
user = self.admin.users.search(email).values[0]
|
||||||
return user.organization.name if user.organization else None
|
return user.organization.name if user.organization else None
|
||||||
|
|
||||||
def create_admin(self) -> Zenpy:
|
def create_admin(self) -> None:
|
||||||
"""
|
"""
|
||||||
Функция создает администратора, проверяя наличие вводимых данных в env.
|
Функция создает администратора, проверяя наличие вводимых данных в env.
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ class ZendeskAdmin:
|
|||||||
raise ValueError('invalid access_controller`s login data')
|
raise ValueError('invalid access_controller`s login data')
|
||||||
|
|
||||||
|
|
||||||
def update_role(user_profile: UserProfile, role: int) -> UserProfile:
|
def update_role(user_profile: UserProfile, role: int) -> None:
|
||||||
"""
|
"""
|
||||||
Функция меняет роль пользователя.
|
Функция меняет роль пользователя.
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ def update_role(user_profile: UserProfile, role: int) -> UserProfile:
|
|||||||
zendesk.admin.users.update(user)
|
zendesk.admin.users.update(user)
|
||||||
|
|
||||||
|
|
||||||
def make_engineer(user_profile: UserProfile, who_changes: User) -> UserProfile:
|
def make_engineer(user_profile: UserProfile, who_changes: User) -> None:
|
||||||
"""
|
"""
|
||||||
Функция устанавливает пользователю роль инженера.
|
Функция устанавливает пользователю роль инженера.
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ def make_engineer(user_profile: UserProfile, who_changes: User) -> UserProfile:
|
|||||||
update_role(user_profile, ROLES['engineer'])
|
update_role(user_profile, ROLES['engineer'])
|
||||||
|
|
||||||
|
|
||||||
def make_light_agent(user_profile: UserProfile, who_changes: User) -> UserProfile:
|
def make_light_agent(user_profile: UserProfile, who_changes: User) -> None:
|
||||||
"""
|
"""
|
||||||
Функция устанавливает пользователю роль легкого агента.
|
Функция устанавливает пользователю роль легкого агента.
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ class CustomRegistrationForm(RegistrationFormUniqueEmail):
|
|||||||
:type visible_fields.email: :class:`django_registration.forms.RegistrationFormUniqueEmail`
|
:type visible_fields.email: :class:`django_registration.forms.RegistrationFormUniqueEmail`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs) -> RegistrationFormUniqueEmail:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
for visible in self.visible_fields():
|
for visible in self.visible_fields():
|
||||||
if visible.field.widget.attrs.get('class', False):
|
if visible.field.widget.attrs.get('class', False):
|
||||||
@ -96,12 +96,11 @@ class StatisticForm(forms.Form):
|
|||||||
:type range_end: :class:`django.forms.fields.DateField`
|
:type range_end: :class:`django.forms.fields.DateField`
|
||||||
"""
|
"""
|
||||||
email = forms.EmailField(
|
email = forms.EmailField(
|
||||||
label='Электроная почта',
|
label='Электронная почта',
|
||||||
widget=forms.EmailInput(
|
widget=forms.EmailInput(
|
||||||
attrs={
|
attrs={
|
||||||
'placeholder': 'example@ngenix.ru',
|
'placeholder': 'example@ngenix.ru',
|
||||||
'class': 'form-control',
|
'class': 'form-control',
|
||||||
'style': 'background-color:#f2f2f2;'
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -15,21 +15,27 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
href="{% url 'profile' %}">Профиль</a>
|
href="{% url 'profile' %}">Профиль</a>
|
||||||
{% if perms.main.has_control_access %}
|
{% if perms.main.has_control_access %}
|
||||||
<a {% if control_lit %}
|
<a {% if control_lit %}
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
{% else %}
|
{% else %}
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
href="{% url 'control' %}">Управление</a>
|
href="{% url 'control' %}">Управление</a>
|
||||||
|
<a {% if stats_lit %}
|
||||||
|
class="btn btn-primary"
|
||||||
|
{% else %}
|
||||||
|
class="btn btn-secondary"
|
||||||
|
{% endif %}
|
||||||
|
href="{% url 'statistic' %}">Статистика</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a {% if work_lit %}
|
<a {% if work_lit %}
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
{% else %}
|
{% else %}
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
href="{% url 'work' request.user.id %}">Запрос прав</a>
|
href="{% url 'work' request.user.id %}">Запрос прав</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="btn btn-secondary" href="{% url 'logout' %}">Выйти</a>
|
<a class="btn btn-secondary" href="{% url 'logout' %}">Выйти</a>
|
||||||
</div>
|
</div>
|
||||||
@ -40,13 +46,13 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
href="/accounts/login">Войти</a>
|
href="/accounts/login">Войти</a>
|
||||||
<a {% if registration_lit %}
|
<a {% if registration_lit %}
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
{% else %}
|
{% else %}
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
href="/accounts/register">Зарегистрироваться</a>
|
href="/accounts/register">Зарегистрироваться</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
<link rel="stylesheet" href="{% static 'main/css/work.css' %}"/>
|
<link rel="stylesheet" href="{% static 'main/css/work.css' %}"/>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_scripts %}
|
{% block extra_scripts %}
|
||||||
@ -21,7 +22,7 @@
|
|||||||
<div class="container-md">
|
<div class="container-md">
|
||||||
|
|
||||||
<div class="new-section">
|
<div class="new-section">
|
||||||
<p class="row page-description">Свободных Мест: {{ licences_remaining }}</p>
|
<p class="row page-description" id="licences_remaining">Свободных Мест:</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% block form %}
|
{% block form %}
|
||||||
@ -37,10 +38,16 @@
|
|||||||
<table class="table table-dark light-table">
|
<table class="table table-dark light-table">
|
||||||
|
|
||||||
<thead>
|
<thead>
|
||||||
|
<th>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="form-check-input"
|
||||||
|
id="head-checkbox"
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Email</th>
|
<th>Email</th>
|
||||||
<th>Role</th>
|
<th>Role</th>
|
||||||
<th>Checked</th>
|
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="tbody"></tbody>
|
<tbody id="tbody"></tbody>
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
{% for radio in form.interval%}
|
{% for radio in form.interval%}
|
||||||
{{ radio.tag }}
|
{{ radio.tag }}
|
||||||
<label class="btn btn-secondary text-primary bg-white" for="{{ radio.id_for_label }}">
|
<label class="btn btn-outline-secondary" for="{{ radio.id_for_label }}">
|
||||||
{{ radio.choice_label }}
|
{{ radio.choice_label }}
|
||||||
</label>
|
</label>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -39,7 +39,7 @@
|
|||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
{% for radio in form.display_format%}
|
{% for radio in form.display_format%}
|
||||||
{{ radio.tag }}
|
{{ radio.tag }}
|
||||||
<label class="btn btn-secondary text-primary bg-white" for="{{ radio.id_for_label }}">
|
<label class="btn btn-outline-secondary" for="{{ radio.id_for_label }}">
|
||||||
{{ radio.choice_label }}
|
{{ radio.choice_label }}
|
||||||
</label>
|
</label>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -67,7 +67,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-row text-center">
|
<div class="form-row text-center">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<button type="submit" class="btn btn-primary bg-white text-primary">Посмотреть статистику</button>
|
<button type="submit" class="btn btn-outline-primary">Посмотреть статистику</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@ -86,9 +86,9 @@
|
|||||||
<table class="table table-bordered text-center text-secondary mt-5" style="background-color:#f2f2f2;">
|
<table class="table table-bordered text-center text-secondary mt-5" style="background-color:#f2f2f2;">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td scope="col">Пользователи/Даты</td>
|
<td scope="col"> </td>
|
||||||
{% for date in log_stats.keys %}
|
{% for date in log_stats.keys %}
|
||||||
<td scope="col">{{date}}</td>
|
<td scope="col">{{ date | date:'d.m' }}</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -96,7 +96,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{ form.email.value }}</td>
|
<td>{{ form.email.value }}</td>
|
||||||
{% for time in log_stats.values %}
|
{% for time in log_stats.values %}
|
||||||
<td>{{time}}</td>
|
<td>{{ time | floatformat:2 }}</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -30,13 +30,17 @@ from .models import UserProfile
|
|||||||
|
|
||||||
|
|
||||||
def setup_context(profile_lit: bool = False, control_lit: bool = False, work_lit: bool = False,
|
def setup_context(profile_lit: bool = False, control_lit: bool = False, work_lit: bool = False,
|
||||||
registration_lit: bool = False, login_lit: bool = False):
|
registration_lit: bool = False, login_lit: bool = False, stats_lit: bool = False):
|
||||||
|
|
||||||
|
print(profile_lit, control_lit, work_lit, registration_lit, login_lit)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'profile_lit': profile_lit,
|
'profile_lit': profile_lit,
|
||||||
'control_lit': control_lit,
|
'control_lit': control_lit,
|
||||||
'work_lit': work_lit,
|
'work_lit': work_lit,
|
||||||
'registration_lit': registration_lit,
|
'registration_lit': registration_lit,
|
||||||
'login_lit': login_lit,
|
'login_lit': login_lit,
|
||||||
|
'stats_lit': stats_lit,
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@ -300,30 +304,6 @@ class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageM
|
|||||||
make_light_agent(user, self.request.user)
|
make_light_agent(user, self.request.user)
|
||||||
log(user, self.request.user.userprofile)
|
log(user, self.request.user.userprofile)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs) -> dict:
|
|
||||||
"""
|
|
||||||
Функция формирования контента страницы администратора (с проверкой прав доступа)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# context = super().get_context_data(**kwargs)
|
|
||||||
|
|
||||||
# context['licences_remaining'] = max(0, ZENDESK_MAX_AGENTS - context['engineers'])
|
|
||||||
# return context
|
|
||||||
|
|
||||||
context = setup_context(control_lit=True)
|
|
||||||
context.update(super().get_context_data(**kwargs))
|
|
||||||
users = get_list_or_404(
|
|
||||||
UserProfile, role='agent')
|
|
||||||
context['engineers'], context['light_agents'] = count_users(get_users_list())
|
|
||||||
context.update({
|
|
||||||
'users': users,
|
|
||||||
'ZENDESK_ROLES': ZENDESK_ROLES,
|
|
||||||
'engineers': context['engineers'],
|
|
||||||
'light_agents': context['light_agents'],
|
|
||||||
'licences_remaining': max(0, ZENDESK_MAX_AGENTS - context['engineers']),
|
|
||||||
})
|
|
||||||
return context # TODO: need to get profile page url
|
|
||||||
|
|
||||||
|
|
||||||
class CustomLoginView(LoginView):
|
class CustomLoginView(LoginView):
|
||||||
"""
|
"""
|
||||||
@ -349,7 +329,8 @@ class UsersViewSet(viewsets.ReadOnlyModelViewSet):
|
|||||||
'users': serializer.data,
|
'users': serializer.data,
|
||||||
'engineers': count[0],
|
'engineers': count[0],
|
||||||
'light_agents': count[1],
|
'light_agents': count[1],
|
||||||
"zendesk_users": self.get_zendesk_users(self.choose_users(users.values, profiles))
|
'zendesk_users': self.get_zendesk_users(self.choose_users(users.values, profiles)),
|
||||||
|
'max_agents': ZENDESK_MAX_AGENTS
|
||||||
}
|
}
|
||||||
return Response(res)
|
return Response(res)
|
||||||
|
|
||||||
@ -386,7 +367,7 @@ def statistic_page(request: WSGIRequest) -> HttpResponse:
|
|||||||
|
|
||||||
if not request.user.is_superuser:
|
if not request.user.is_superuser:
|
||||||
return redirect('index')
|
return redirect('index')
|
||||||
context = setup_context()
|
context = setup_context(stats_lit=True)
|
||||||
context.update({
|
context.update({
|
||||||
'pagename': 'страница статистики',
|
'pagename': 'страница статистики',
|
||||||
'errors': list(),
|
'errors': list(),
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
function head_checkbox() {
|
||||||
|
let head_checkbox = document.getElementById("head-checkbox");
|
||||||
|
head_checkbox.addEventListener("click", () => {
|
||||||
|
let checkboxes = document.getElementsByName("users");
|
||||||
|
for (let checkbox of checkboxes) checkbox.click();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// React
|
// React
|
||||||
class ModelUserTableRow extends React.Component {
|
class ModelUserTableRow extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<tr className={"table-dark"}>
|
<tr className={"table-dark"}>
|
||||||
<td>
|
|
||||||
<a href="#">{this.props.user.name}</a>
|
|
||||||
</td>
|
|
||||||
<td>{this.props.user.user.email}</td>
|
|
||||||
<td>{this.props.user.zendesk_role}</td>
|
|
||||||
<td>
|
<td>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@ -18,6 +21,11 @@ class ModelUserTableRow extends React.Component {
|
|||||||
name="users"
|
name="users"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="#">{this.props.user.name}</a>
|
||||||
|
</td>
|
||||||
|
<td>{this.props.user.user.email}</td>
|
||||||
|
<td>{this.props.user.zendesk_role}</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -38,12 +46,12 @@ class ZendeskUserTableRow extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<tr className={"table-secondary"}>
|
<tr className={"table-secondary"}>
|
||||||
|
<td></td>
|
||||||
<td>
|
<td>
|
||||||
<a href="#">{this.props.user.name}</a>
|
<a href="#">{this.props.user.name}</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{this.props.user.email}</td>
|
<td>{this.props.user.email}</td>
|
||||||
<td>{this.props.user.zendesk_role}</td>
|
<td>{this.props.user.zendesk_role}</td>
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -68,9 +76,22 @@ class TableBody extends React.Component {
|
|||||||
engineers: 0,
|
engineers: 0,
|
||||||
light_agents: 0,
|
light_agents: 0,
|
||||||
zendesk_users: [],
|
zendesk_users: [],
|
||||||
|
max_agents: 3,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
change_elemnts_html() {
|
||||||
|
let elements = document.querySelectorAll(".info-quantity-value");
|
||||||
|
let licences = document.getElementById("licences_remaining");
|
||||||
|
elements[0].innerHTML = this.state.engineers;
|
||||||
|
elements[1].innerHTML = this.state.light_agents;
|
||||||
|
let max_licences = Math.max(
|
||||||
|
this.state.max_agents - this.state.engineers,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
licences.innerHTML = "Свободных мест: " + max_licences;
|
||||||
|
}
|
||||||
|
|
||||||
async get_users() {
|
async get_users() {
|
||||||
await axios.get("/api/users").then((response) => {
|
await axios.get("/api/users").then((response) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -78,11 +99,10 @@ class TableBody extends React.Component {
|
|||||||
engineers: response.data.engineers,
|
engineers: response.data.engineers,
|
||||||
light_agents: response.data.light_agents,
|
light_agents: response.data.light_agents,
|
||||||
zendesk_users: response.data.zendesk_users,
|
zendesk_users: response.data.zendesk_users,
|
||||||
|
max_agents: response.data.max_agents,
|
||||||
});
|
});
|
||||||
let elements = document.querySelectorAll(".info-quantity-value");
|
|
||||||
elements[0].innerHTML = this.state.engineers;
|
|
||||||
elements[1].innerHTML = this.state.light_agents;
|
|
||||||
});
|
});
|
||||||
|
this.change_elemnts_html();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_pretext() {
|
delete_pretext() {
|
||||||
@ -111,3 +131,4 @@ class TableBody extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReactDOM.render(<TableBody />, document.getElementById("tbody"));
|
ReactDOM.render(<TableBody />, document.getElementById("tbody"));
|
||||||
|
head_checkbox();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user