Merge branch 'develop' into feature/dockerbuild

This commit is contained in:
Artyom Kravchenko 2021-04-19 14:46:12 +03:00
commit b4c0ab0d67
8 changed files with 73 additions and 56 deletions

View File

@ -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

View File

@ -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:
""" """
Функция устанавливает пользователю роль легкого агента. Функция устанавливает пользователю роль легкого агента.

View File

@ -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;'
} }
), ),
) )

View File

@ -23,6 +23,12 @@
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"

View File

@ -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>

View File

@ -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">&nbsp;</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>

View File

@ -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(),

View File

@ -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();