experiments

This commit is contained in:
Administrator 2023-09-23 18:54:49 +03:00
parent 4991376d93
commit 59aa298db6
13 changed files with 200 additions and 2 deletions

View File

@ -46,7 +46,8 @@ INSTALLED_APPS = [
"django.contrib.messages", "django.contrib.messages",
"django.contrib.staticfiles", "django.contrib.staticfiles",
'web.apps.WebConfig', 'web.apps.WebConfig',
'configs.apps.ConfigsConfig' 'configs.apps.ConfigsConfig',
'experiments.apps.ExperimentsConfig'
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View File

@ -19,5 +19,6 @@ from django.urls import path, include
urlpatterns = [ urlpatterns = [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path('configs/', include('configs.urls')), path('configs/', include('configs.urls')),
path('experiments/', include('experiments.urls')),
path('', include('web.urls')) path('', include('web.urls'))
] ]

0
experiments/__init__.py Normal file
View File

3
experiments/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
experiments/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ExperimentsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'experiments'

View File

@ -0,0 +1,35 @@
# Generated by Django 4.1.7 on 2023-09-23 15:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('web', '0004_alter_customuser_vk_id_alter_customuser_yandex_id'),
]
operations = [
migrations.CreateModel(
name='Experiment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField()),
('enabled', models.BooleanField(default=False)),
('condition', models.TextField(default='False')),
('stage', models.TextField(default='production')),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.project')),
],
),
migrations.AddIndex(
model_name='experiment',
index=models.Index(fields=['project', 'stage'], name='experiments_project_e2c9f6_idx'),
),
migrations.AddIndex(
model_name='experiment',
index=models.Index(fields=['project', 'name', 'stage'], name='experiments_project_dade49_idx'),
),
]

View File

29
experiments/models.py Normal file
View File

@ -0,0 +1,29 @@
from django.db import models
# Create your models here.
class Experiment(models.Model):
name = models.TextField()
enabled = models.BooleanField(default=False)
condition = models.TextField(default='False')
project = models.ForeignKey('web.Project', on_delete=models.CASCADE)
stage = models.TextField(default='production')
class Meta:
indexes = [
models.Index(fields=['project', 'stage']),
models.Index(fields=['project', 'name', 'stage'])
]
@property
def exp_type(self):
if self.condition == 'False':
return 0
if self.condition == 'user.is_superuser':
return 1
if self.condition == 'user.is_staff':
return 2
if self.condition == 'True':
return 3
return 4

3
experiments/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

8
experiments/urls.py Normal file
View File

@ -0,0 +1,8 @@
from django.contrib import admin
from django.urls import path
from .views import *
urlpatterns = [
path(*ExperimentsView.as_path())
]

42
experiments/views.py Normal file
View File

@ -0,0 +1,42 @@
from BaseLib.BaseView import BaseView
from experiments.models import Experiment
class ExperimentsView(BaseView):
required_login = True
endpoint = ''
view_file = 'experiments.html'
def pre_handle(self):
if 'stage' not in self.request.GET or self.request.GET['stage'] not in ['development', 'production']:
return '/experiments/?stage=production'
self.stage = self.request.GET['stage']
self.context['stage'] = self.stage
def get(self):
self.context['experiments'] = Experiment.objects.filter(project=self.request.user.selected_project,
stage=self.stage).order_by('name')
def post_create(self):
Experiment.objects.create(project=self.request.user.selected_project, stage=self.stage, name=self.request.POST['name'])
return '/experiments/?stage=' + self.stage
def post_delete(self):
Experiment.objects.get(id=self.request.POST['experiment_id']).delete()
return '/experiments/?stage=' + self.stage
def post_change(self):
exp = Experiment.objects.get(id=self.request.POST['experiment_id'])
exp.enabled = 'enabled' in self.request.POST
condition = self.request.POST['condition_select']
if condition == 'Другое (только для техлидов)':
exp.condition = self.request.POST['condition']
elif condition == 'Никому':
exp.condition = 'False'
elif condition == 'Только админам':
exp.condition = 'user.is_superuser'
elif condition == 'Только сотрудникам':
exp.condition = 'user.is_staff'
else:
exp.condition = 'True'
exp.save()
return '/experiments/?stage=' + self.stage

View File

@ -0,0 +1,70 @@
{% extends 'layouts/base.html' %}
{% block javascripts %}
<script>
function onSelect(experimentName) {
const selectElement = document.getElementById('select_' + experimentName);
const inputElement = document.getElementById('input_' + experimentName);
inputElement.hidden = selectElement.value !== 'Другое (только для техлидов)'
}
</script>
{% endblock %}
{% block content %}
<h1>Эксперименты <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#create_experiment">+</button></h1>
<div class="modal fade" id="create_experiment" tabindex="-1" aria-labelledby="modal-default" style="display: none;" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<form method="POST">
{% csrf_token %}
<div class="modal-header">
<h2 class="h6 modal-title">Создать эксперимент</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="text" name="name" placeholder="Название эксперимента" style="width: 100%;" />
</div>
<div class="modal-footer">
<button type="submit" name="action" value="create" class="btn btn-secondary">Создать</button>
<button type="button" class="btn btn-link text-gray-600 ms-auto" data-bs-dismiss="modal">Закрыть</button>
</div>
</form>
</div>
</div>
</div>
<select class="form-select" style="width: 150px;" onchange="window.location.href='/experiments/?stage=' + this.value;">
<option {% if stage == 'production' %}selected{% endif %}>production</option>
<option {% if stage == 'development' %}selected{% endif %}>development</option>
</select>
<table class="table">
<thead>
<th>Название эксперимента</th>
<th>Включен</th>
<th>Условие</th>
<th>Действие</th>
</thead>
<tbody>
{% for experiment in experiments %}
<tr>
<form method="POST">
{% csrf_token %}
<input type="hidden" name="experiment_id" value="{{ experiment.id }}" />
<td>{{ experiment.name }}<input type="hidden" name="name" value="{{ experiment.name }}"/> </td>
<td><input type="checkbox" name="enabled" {% if experiment.enabled %}checked{% endif %}> </td>
<td>
<select id="select_{{ experiment.name }}" class="form-select" name="condition_select" onchange="onSelect('{{ experiment.name }}')">
<option {% if experiment.exp_type == 0 %}selected{% endif %}>Никому</option>
<option {% if experiment.exp_type == 1 %}selected{% endif %}>Только админам</option>
<option {% if experiment.exp_type == 2 %}selected{% endif %}>Только сотрудникам</option>
<option {% if experiment.exp_type == 3 %}selected{% endif %}>Всем</option>
<option {% if experiment.exp_type == 4 %}selected{% endif %}>Другое (только для техлидов)</option>
</select>
<input id="input_{{ experiment.name }}" name="condition" {% if experiment.exp_type != 4%}hidden{% endif %} type="text" class="form-control" value="{{ experiment.condition }}" />
</td>
<td><button type="submit" name="action" value="change" class="btn btn-primary">Применить</button> <button type="submit" name="action" value="delete" class="btn btn-danger">Удалить</button></td>
</form>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -83,7 +83,7 @@
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a href="/profile" class="nav-link"> <a href="/experiments" class="nav-link">
<span class="sidebar-icon"> <span class="sidebar-icon">
<i class="fa fa-flask"></i> <i class="fa fa-flask"></i>
</span> </span>