Compare commits

...

26 Commits

Author SHA1 Message Date
bfe281fb05 Merge pull request 'master' (#18) from master into prod
Reviewed-on: #18
2025-09-18 23:50:59 +03:00
f8b700b982 Update .deploy/deploy-prod.yaml
All checks were successful
Deploy Dev / Build (pull_request) Successful in 1m12s
Deploy Dev / Push (pull_request) Successful in 28s
Deploy Dev / Deploy dev (pull_request) Successful in 26s
Deploy Prod / Build (pull_request) Successful in 7s
Deploy Prod / Push (pull_request) Successful in 11s
Deploy Prod / Deploy prod (pull_request) Successful in 13s
2025-09-18 23:45:10 +03:00
c96ad735d7 Update .deploy/deploy-dev.yaml 2025-09-18 23:44:00 +03:00
ce266c879c Merge pull request 'master' (#16) from master into prod
Reviewed-on: #16
2025-09-14 09:58:52 +03:00
ddb8b97fd4 Update .deploy/deploy-prod.yaml
All checks were successful
Deploy Prod / Build (pull_request) Successful in 59s
Deploy Prod / Push (pull_request) Successful in 30s
Deploy Prod / Deploy prod (pull_request) Successful in 13s
2025-09-14 09:58:41 +03:00
e014b1f526 Update .deploy/deploy-dev.yaml 2025-09-14 09:57:54 +03:00
4bffbaa71c Merge pull request 'fix' (#15) from master into prod
Reviewed-on: #15
2024-12-08 15:50:42 +03:00
f1b57abeca fix
All checks were successful
Deploy Prod / Build (pull_request) Successful in 6s
Deploy Prod / Push (pull_request) Successful in 13s
Deploy Prod / Deploy prod (pull_request) Successful in 14s
2024-12-08 15:50:16 +03:00
d756179afc Merge pull request 'master' (#14) from master into prod
Reviewed-on: #14
2024-12-08 15:17:34 +03:00
b9a2ab5509 Merge branch 'master' of https://gitea.sprinthub.ru/self/platform
All checks were successful
Deploy Prod / Build (pull_request) Successful in 7s
Deploy Prod / Push (pull_request) Successful in 10s
Deploy Prod / Deploy prod (pull_request) Successful in 15s
2024-12-08 15:16:56 +03:00
8fb349d74b fix 2024-12-08 15:16:43 +03:00
eec0828dda Merge pull request 'Update stats/management/commands/fetch_stats.py' (#13) from master into prod
Reviewed-on: #13
2024-12-02 15:13:51 +03:00
7238b50ee6 Update stats/management/commands/fetch_stats.py
All checks were successful
Deploy Prod / Build (pull_request) Successful in 7s
Deploy Prod / Push (pull_request) Successful in 10s
Deploy Prod / Deploy prod (pull_request) Successful in 14s
2024-12-02 15:13:32 +03:00
2a30408bf0 Merge pull request 'master' (#12) from master into prod
Reviewed-on: #12
2024-11-27 02:27:20 +03:00
ed33722449 fix
All checks were successful
Deploy Prod / Build (pull_request) Successful in 5s
Deploy Prod / Push (pull_request) Successful in 9s
Deploy Prod / Deploy prod (pull_request) Successful in 12s
2024-11-27 02:26:44 +03:00
a79f6cd9f0 Merge pull request 'config' (#11) from config into master
Reviewed-on: #11
2024-11-24 23:49:25 +03:00
5275687b26 Merge pull request 'config' (#10) from config into prod
Reviewed-on: #10
2024-11-24 23:40:06 +03:00
f47f0d61ca fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 6s
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Successful in 11s
Deploy Prod / Build (pull_request) Successful in 6s
Deploy Prod / Push (pull_request) Successful in 9s
Deploy Prod / Deploy prod (pull_request) Successful in 12s
2024-11-24 23:30:03 +03:00
fba06976d4 fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 6s
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Successful in 11s
2024-11-24 22:49:23 +03:00
965a015c51 fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 6s
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Successful in 10s
2024-11-24 12:28:06 +03:00
3045e8334a fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 6s
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Successful in 12s
2024-11-24 12:24:14 +03:00
2b739efb3a fix
All checks were successful
Deploy Dev / Build (pull_request) Successful in 6s
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Successful in 13s
2024-11-24 12:08:15 +03:00
f3e0b835d1 fix
Some checks failed
Deploy Dev / Build (pull_request) Successful in 6s
Deploy Dev / Push (pull_request) Successful in 9s
Deploy Dev / Deploy dev (pull_request) Failing after 6s
2024-11-24 12:06:30 +03:00
fb299a7908 config 2024-11-24 12:05:09 +03:00
ff45d77f44 Merge pull request 'Update .deploy/deploy-prod.yaml' (#3) from master into prod
Reviewed-on: #3
2024-10-11 16:29:00 +03:00
c6388b04b2 Update .deploy/deploy-prod.yaml
All checks were successful
Deploy Prod / Build (pull_request) Successful in 6s
Deploy Prod / Push (pull_request) Successful in 10s
Deploy Prod / Deploy prod (pull_request) Successful in 16s
2024-10-11 16:28:40 +03:00
29 changed files with 461 additions and 62 deletions

View File

@@ -6,16 +6,17 @@ services:
platform-nginx: platform-nginx:
image: mathwave/sprint-repo:platform image: mathwave/sprint-repo:platform
networks: networks:
- common-infra-nginx - common-infra-nginx-development
- configurator-development
- postgres-development
- minio-development
environment: environment:
DB_HOST: "pg.develop.sprinthub.ru" DB_HOST: "postgres"
DB_PASSWORD: $DB_PASSWORD_DEV DB_PASSWORD: $DB_PASSWORD_DEV
MINIO_HOST: "minio.develop.sprinthub.ru" MINIO_HOST: "minio"
MINIO_SECRET_KEY: $MINIO_SECRET_KEY_DEV MINIO_SECRET_KEY: $MINIO_SECRET_KEY_DEV
REDIS_HOST: "redis.develop.sprinthub.ru" REDIS_HOST: "redis.dev.chocomarsh.com"
REDIS_PASSWORD: $REDIS_PASSWORD_DEV REDIS_PASSWORD: $REDIS_PASSWORD_DEV
RABBITMQ_HOST: "rabbitmq.develop.sprinthub.ru"
RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD_DEV
VK_SERVICE_TOKEN: $VK_SERVICE_TOKEN VK_SERVICE_TOKEN: $VK_SERVICE_TOKEN
YANDEX_SERVICE_TOKEN: $YANDEX_SERVICE_TOKEN YANDEX_SERVICE_TOKEN: $YANDEX_SERVICE_TOKEN
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
@@ -36,15 +37,16 @@ services:
fetch_stats: fetch_stats:
image: mathwave/sprint-repo:platform image: mathwave/sprint-repo:platform
networks:
- postgres-development
- minio-development
environment: environment:
DB_HOST: "pg.develop.sprinthub.ru" DB_HOST: "postgres"
DB_PASSWORD: $DB_PASSWORD_DEV DB_PASSWORD: $DB_PASSWORD_DEV
MINIO_HOST: "minio.develop.sprinthub.ru" MINIO_HOST: "minio"
MINIO_SECRET_KEY: $MINIO_SECRET_KEY_DEV MINIO_SECRET_KEY: $MINIO_SECRET_KEY_DEV
REDIS_HOST: "redis.develop.sprinthub.ru" REDIS_HOST: "redis.dev.chocomarsh.com"
REDIS_PASSWORD: $REDIS_PASSWORD_DEV REDIS_PASSWORD: $REDIS_PASSWORD_DEV
RABBITMQ_HOST: "rabbitmq.develop.sprinthub.ru"
RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD_DEV
VK_SERVICE_TOKEN: $VK_SERVICE_TOKEN VK_SERVICE_TOKEN: $VK_SERVICE_TOKEN
YANDEX_SERVICE_TOKEN: $YANDEX_SERVICE_TOKEN YANDEX_SERVICE_TOKEN: $YANDEX_SERVICE_TOKEN
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
@@ -59,11 +61,19 @@ services:
migrate: migrate:
image: mathwave/sprint-repo:platform image: mathwave/sprint-repo:platform
networks:
- postgres-development
- minio-development
environment: environment:
DB_HOST: "pg.develop.sprinthub.ru" DB_HOST: "postgres"
DB_PASSWORD: $DB_PASSWORD_DEV DB_PASSWORD: $DB_PASSWORD_DEV
MINIO_HOST: "minio.develop.sprinthub.ru" MINIO_HOST: "minio"
MINIO_SECRET_KEY: $MINIO_SECRET_KEY_DEV MINIO_SECRET_KEY: $MINIO_SECRET_KEY_DEV
REDIS_HOST: "redis.dev.chocomarsh.com"
REDIS_PASSWORD: $REDIS_PASSWORD_DEV
VK_SERVICE_TOKEN: $VK_SERVICE_TOKEN
YANDEX_SERVICE_TOKEN: $YANDEX_SERVICE_TOKEN
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
command: migrate command: migrate
deploy: deploy:
mode: replicated mode: replicated
@@ -74,5 +84,11 @@ services:
order: start-first order: start-first
networks: networks:
common-infra-nginx: common-infra-nginx-development:
external: true
configurator-development:
external: true
postgres-development:
external: true
minio-development:
external: true external: true

View File

@@ -7,18 +7,20 @@ services:
image: mathwave/sprint-repo:platform image: mathwave/sprint-repo:platform
networks: networks:
- common-infra-nginx - common-infra-nginx
- configurator
- postgres
- minio
environment: environment:
DB_HOST: "pg.sprinthub.ru" DB_HOST: "postgres"
DB_PASSWORD: $DB_PASSWORD_PROD DB_PASSWORD: $DB_PASSWORD_PROD
MINIO_HOST: "minio.sprinthub.ru" MINIO_HOST: "minio"
MINIO_SECRET_KEY: $MINIO_SECRET_KEY_PROD MINIO_SECRET_KEY: $MINIO_SECRET_KEY_PROD
REDIS_HOST: "redis.sprinthub.ru" REDIS_HOST: "redis.chocomarsh.com"
REDIS_PASSWORD: $REDIS_PASSWORD_PROD REDIS_PASSWORD: $REDIS_PASSWORD_PROD
RABBITMQ_HOST: "rabbitmq.sprinthub.ru" RABBITMQ_HOST: "rabbitmq.chocomarsh.com"
RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD_PROD RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD_PROD
VK_SERVICE_TOKEN: $VK_SERVICE_TOKEN VK_SERVICE_TOKEN: $VK_SERVICE_TOKEN
YANDEX_SERVICE_TOKEN: $YANDEX_SERVICE_TOKEN YANDEX_SERVICE_TOKEN: $YANDEX_SERVICE_TOKEN
TELEGRAM_TOKEN: $TELEGRAM_TOKEN
PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN PLATFORM_SECURITY_TOKEN: $PLATFORM_SECURITY_TOKEN
command: runserver 0.0.0.0:1238 command: runserver 0.0.0.0:1238
healthcheck: healthcheck:
@@ -31,24 +33,23 @@ services:
mode: replicated mode: replicated
restart_policy: restart_policy:
condition: any condition: any
placement:
constraints:
- node.role == worker
- node.labels.zone == ru
update_config: update_config:
parallelism: 1 parallelism: 1
order: start-first order: start-first
fetch_stats: fetch_stats:
image: mathwave/sprint-repo:platform image: mathwave/sprint-repo:platform
networks:
- postgres
- minio
environment: environment:
DB_HOST: "pg.sprinthub.ru" DB_HOST: "postgres"
DB_PASSWORD: $DB_PASSWORD_PROD DB_PASSWORD: $DB_PASSWORD_PROD
MINIO_HOST: "minio.sprinthub.ru" MINIO_HOST: "minio"
MINIO_SECRET_KEY: $MINIO_SECRET_KEY_PROD MINIO_SECRET_KEY: $MINIO_SECRET_KEY_PROD
REDIS_HOST: "redis.sprinthub.ru" REDIS_HOST: "redis.chocomarsh.com"
REDIS_PASSWORD: $REDIS_PASSWORD_PROD REDIS_PASSWORD: $REDIS_PASSWORD_PROD
RABBITMQ_HOST: "rabbitmq.sprinthub.ru" RABBITMQ_HOST: "rabbitmq.chocomarsh.com"
RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD_PROD RABBITMQ_PASSWORD: $RABBITMQ_PASSWORD_PROD
VK_SERVICE_TOKEN: $VK_SERVICE_TOKEN VK_SERVICE_TOKEN: $VK_SERVICE_TOKEN
YANDEX_SERVICE_TOKEN: $YANDEX_SERVICE_TOKEN YANDEX_SERVICE_TOKEN: $YANDEX_SERVICE_TOKEN
@@ -64,12 +65,14 @@ services:
migrate: migrate:
image: mathwave/sprint-repo:platform image: mathwave/sprint-repo:platform
networks:
- postgres
- minio
environment: environment:
DB_HOST: "pg.sprinthub.ru" DB_HOST: "postgres"
DB_PASSWORD: $DB_PASSWORD_PROD DB_PASSWORD: $DB_PASSWORD_PROD
MINIO_HOST: "minio.sprinthub.ru" MINIO_HOST: "minio"
MINIO_SECRET_KEY: $MINIO _SECRET_KEY_PROD MINIO_SECRET_KEY: $MINIO _SECRET_KEY_PROD
TELEGRAM_TOKEN: $TELEGRAM_TOKEN
command: migrate command: migrate
deploy: deploy:
mode: replicated mode: replicated
@@ -82,3 +85,9 @@ services:
networks: networks:
common-infra-nginx: common-infra-nginx:
external: true external: true
configurator:
external: true
postgres:
external: true
minio:
external: true

View File

@@ -28,7 +28,7 @@ jobs:
run: docker push mathwave/sprint-repo:platform run: docker push mathwave/sprint-repo:platform
deploy-dev: deploy-dev:
name: Deploy dev name: Deploy dev
runs-on: [dev] runs-on: [prod]
needs: push needs: push
steps: steps:
- name: login - name: login
@@ -46,4 +46,4 @@ jobs:
RABBITMQ_PASSWORD: ${{ secrets.RABBITMQ_PASSWORD_DEV }} RABBITMQ_PASSWORD: ${{ secrets.RABBITMQ_PASSWORD_DEV }}
VK_SERVICE_TOKEN: ${{ secrets.VK_SERVICE_TOKEN }} VK_SERVICE_TOKEN: ${{ secrets.VK_SERVICE_TOKEN }}
YANDEX_SERVICE_TOKEN: ${{ secrets.YANDEX_SERVICE_TOKEN }} YANDEX_SERVICE_TOKEN: ${{ secrets.YANDEX_SERVICE_TOKEN }}
run: docker stack deploy --with-registry-auth -c ./.deploy/deploy-dev.yaml platform run: docker stack deploy --with-registry-auth -c ./.deploy/deploy-dev.yaml platform-development

58
BaseLib/configurator.py Normal file
View File

@@ -0,0 +1,58 @@
from requests import get, put, post, delete
from json import dumps
URL_CONFIGS = 'http://configurator/api/v1/configs'
URL_EXPERIMENTS = 'http://configurator/api/v1/experiments'
def get_configs(project, stage):
response = get(URL_CONFIGS, params={'project': project, 'stage': stage})
if response.status_code != 200:
return []
data = response.json()
for config in data:
config['value_pretty'] = dumps(config['value'], indent=4, ensure_ascii=False)
return data
def create_config(project, stage, name):
post(URL_CONFIGS, json={'project': project, 'stage': stage, 'name': name})
def update_config(id, value):
put(URL_CONFIGS, json={'id': id, 'value': value})
def delete_config(id):
delete(URL_CONFIGS, json={'id': id})
def get_experiments(project, stage):
response = get(URL_EXPERIMENTS, params={'project': project, 'stage': stage})
if response.status_code != 200:
return []
data = response.json()
for exp in data:
if exp['condition'] == 'False':
exp['exp_type'] = 0
elif exp['condition'] == 'user.is_superuser':
exp['exp_type'] = 1
elif exp['condition'] == 'user.platform_staff':
exp['exp_type'] = 2
elif exp['condition'] == 'True':
exp['exp_type'] = 3
else:
exp['exp_type'] = 4
return data
def create_experiment(project, stage, name):
post(URL_EXPERIMENTS, json={'project': project, 'stage': stage, 'name': name})
def update_experiment(id, enabled, condition):
put(URL_EXPERIMENTS, json={'id': id, 'enabled': enabled, 'condition': condition})
def delete_experiment(id):
delete(URL_CONFIGS, json={'id': id})

View File

@@ -48,7 +48,8 @@ INSTALLED_APPS = [
'web.apps.WebConfig', 'web.apps.WebConfig',
'configs.apps.ConfigsConfig', 'configs.apps.ConfigsConfig',
'experiments.apps.ExperimentsConfig', 'experiments.apps.ExperimentsConfig',
'stats.apps.StatsConfig' 'stats.apps.StatsConfig',
'schemas.apps.SchemasConfig',
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View File

@@ -21,5 +21,6 @@ urlpatterns = [
path('configs/', include('configs.urls')), path('configs/', include('configs.urls')),
path('experiments/', include('experiments.urls')), path('experiments/', include('experiments.urls')),
path('stats/', include('stats.urls')), path('stats/', include('stats.urls')),
path('schemas/', include('schemas.urls')),
path('', include('web.urls')) path('', include('web.urls'))
] ]

View File

@@ -3,6 +3,7 @@ from json import loads
from django.http import HttpResponse, JsonResponse from django.http import HttpResponse, JsonResponse
from BaseLib.BaseView import BaseView from BaseLib.BaseView import BaseView
from BaseLib.configurator import *
from Platform import settings from Platform import settings
from configs.models import Config from configs.models import Config
@@ -20,31 +21,35 @@ class ConfigsView(BaseView):
return '/configs/?stage=production' return '/configs/?stage=production'
self.stage = self.request.GET['stage'] self.stage = self.request.GET['stage']
self.context['stage'] = self.stage self.context['stage'] = self.stage
self.context['configs'] = Config.objects.filter(project=self.request.user.selected_project, # self.context['configs'] = Config.objects.filter(project=self.request.user.selected_project,
stage=self.stage).order_by('name') # stage=self.stage).order_by('name')
self.context['configs'] = get_configs(self.request.user.selected_project.name, self.stage)
def post_create_config(self): def post_create_config(self):
Config.objects.create(name=self.request.POST['name'], project=self.request.user.selected_project, stage=self.stage) create_config(self.request.user.selected_project.name, self.stage, self.request.POST['name'])
# Config.objects.create(name=self.request.POST['name'], project=self.request.user.selected_project, stage=self.stage)
return '/configs/?stage=' + self.stage return '/configs/?stage=' + self.stage
def post_delete(self): def post_delete(self):
config = Config.objects.get(id=self.request.POST['config']) # config = Config.objects.get(id=self.request.POST['config'])
config.delete() # config.delete()
return '/configs/?stage=' + config.stage delete_config(self.request.POST['config'])
return '/configs/?stage=' + self.stage
def post_save(self): def post_save(self):
data = self.request.POST['data'] data = self.request.POST['data']
config = Config.objects.get(id=self.request.POST['config']) # config = Config.objects.get(id=self.request.POST['config'])
try: try:
data = loads(data) data = loads(data)
except: except:
self.context['incorrect_config'] = config self.context['incorrect_config'] = self.request.POST['config']
self.context['incorrect_data'] = data self.context['incorrect_data'] = data
self.context['error'] = True self.context['error'] = True
return return
config.data = data update_config(self.request.POST['config'], data)
config.save() # config.data = data
return '/configs/?stage=' + config.stage # config.save()
return '/configs/?stage=' + self.stage
def get_config(request): def get_config(request):

View File

@@ -1,6 +1,7 @@
from django.http import HttpResponse, JsonResponse from django.http import HttpResponse, JsonResponse
from BaseLib.BaseView import BaseView from BaseLib.BaseView import BaseView
from BaseLib.configurator import *
from Platform import settings from Platform import settings
from experiments.models import Experiment from experiments.models import Experiment
@@ -17,31 +18,48 @@ class ExperimentsView(BaseView):
self.context['stage'] = self.stage self.context['stage'] = self.stage
def get(self): def get(self):
self.context['experiments'] = Experiment.objects.filter(project=self.request.user.selected_project, # self.context['experiments'] = Experiment.objects.filter(project=self.request.user.selected_project,
stage=self.stage).order_by('name') # stage=self.stage).order_by('name')
self.context['experiments'] = get_experiments(self.request.user.selected_project.name, self.stage)
def post_create(self): def post_create(self):
Experiment.objects.create(project=self.request.user.selected_project, stage=self.stage, name=self.request.POST['name']) # Experiment.objects.create(project=self.request.user.selected_project, stage=self.stage, name=self.request.POST['name'])
create_experiment(self.request.user.selected_project.name, self.stage, self.request.POST['name'])
return '/experiments/?stage=' + self.stage return '/experiments/?stage=' + self.stage
def post_delete(self): def post_delete(self):
Experiment.objects.get(id=self.request.POST['experiment_id']).delete() # Experiment.objects.get(id=self.request.POST['experiment_id']).delete()
delete_experiment(self.request.POST['experiment_id'])
return '/experiments/?stage=' + self.stage return '/experiments/?stage=' + self.stage
def post_change(self): def post_change(self):
exp = Experiment.objects.get(id=self.request.POST['experiment_id']) # exp = Experiment.objects.get(id=self.request.POST['experiment_id'])
exp.enabled = 'enabled' in self.request.POST # 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.platform_staff'
# else:
# exp.condition = 'True'
# exp.save()
enabled = 'enabled' in self.request.POST
condition = self.request.POST['condition_select'] condition = self.request.POST['condition_select']
if condition == 'Другое (только для техлидов)': if condition == 'Другое (только для техлидов)':
exp.condition = self.request.POST['condition'] condition = self.request.POST['condition']
elif condition == 'Никому': elif condition == 'Никому':
exp.condition = 'False' condition = 'False'
elif condition == 'Только админам': elif condition == 'Только админам':
exp.condition = 'user.is_superuser' condition = 'user.is_superuser'
elif condition == 'Только сотрудникам': elif condition == 'Только сотрудникам':
exp.condition = 'user.platform_staff' condition = 'user.platform_staff'
else: else:
exp.condition = 'True' condition = 'True'
exp.save() update_experiment(self.request.POST['experiment_id'], enabled, condition)
return '/experiments/?stage=' + self.stage return '/experiments/?stage=' + self.stage

37
generator.py Normal file
View File

@@ -0,0 +1,37 @@
import json
import urllib.request
import os
import shutil
projects = {
'queues': 'tasks.proto'
}
try:
shutil.rmtree('schemas')
except:
pass
try:
os.mkdir('schemas')
except:
pass
for project in projects:
response = urllib.request.urlopen(f'https://platform.sprinthub.ru/schemas/get?project={project}').read()
data = json.loads(response)
os.mkdir(f'schemas/{project}')
for key, value in data.items():
with open(f'schemas/{project}/{key}', 'w+') as fp:
fp.write(value)
for key, value in projects.items():
os.system(f'python -m grpc_tools.protoc --proto_path schemas --python_out=. --pyi_out=. --grpc_python_out=. ./schemas/{key}/{value}')
try:
shutil.rmtree('schemas')
except:
pass

0
schemas/__init__.py Normal file
View File

6
schemas/admin.py Normal file
View File

@@ -0,0 +1,6 @@
from django.contrib import admin
# Register your models here.
from .models import Schema
admin.site.register(Schema)

6
schemas/apps.py Normal file
View File

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

View File

@@ -0,0 +1,28 @@
# Generated by Django 5.1.4 on 2024-12-08 11:56
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('web', '0007_customuser_telegram_id_customuser_telegram_username'),
]
operations = [
migrations.CreateModel(
name='Schema',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField()),
('data', models.BinaryField(blank=True, null=True)),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.project')),
],
options={
'indexes': [models.Index(fields=['project'], name='schemas_sch_project_18fee8_idx')],
},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.4 on 2024-12-08 12:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('schemas', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='schema',
name='data',
field=models.TextField(blank=True, null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.4 on 2024-12-08 12:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('schemas', '0002_alter_schema_data'),
]
operations = [
migrations.AlterField(
model_name='schema',
name='data',
field=models.TextField(blank=True, default=''),
),
]

View File

14
schemas/models.py Normal file
View File

@@ -0,0 +1,14 @@
from django.db import models
# Create your models here.
class Schema(models.Model):
project = models.ForeignKey('web.Project', on_delete=models.CASCADE)
name = models.TextField()
data = models.TextField(default='', blank=True)
class Meta:
indexes = [
models.Index(fields=['project'])
]

3
schemas/tests.py Normal file
View File

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

9
schemas/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.contrib import admin
from django.urls import path
from .views import *
urlpatterns = [
path(*SchemasView.as_path()),
path('get', get_schemas)
]

46
schemas/views.py Normal file
View File

@@ -0,0 +1,46 @@
from json import loads
from django.http import HttpResponse, JsonResponse
from BaseLib.BaseView import BaseView
from BaseLib.configurator import *
from Platform import settings
from schemas.models import Schema
# Create your views here.
class SchemasView(BaseView):
required_login = True
endpoint = ''
view_file = 'schemas.html'
def pre_handle(self):
self.context['schemas'] = Schema.objects.filter(project=self.request.user.selected_project)
def post_create_schema(self):
Schema.objects.create(project=self.request.user.selected_project, name=self.request.POST['name'])
return '/schemas'
def post_delete(self):
Schema.objects.get(id=self.request.POST['schema']).delete()
return '/schemas'
def post_save(self):
schema = Schema.objects.get(id=self.request.POST['schema'])
schema.data = self.request.POST['data']
schema.save()
return '/schemas'
def get_schemas(request):
project = request.GET.get('project')
if project is None:
return HttpResponse('', status=400)
data = {
schema.name: schema.data
for schema in Schema.objects.filter(project__name=project)
}
return JsonResponse(data, safe=False)

View File

@@ -24,6 +24,8 @@ class Command(BaseCommand):
if response.status_code != 200: if response.status_code != 200:
continue continue
Snapshot.objects.create(project=project, data=response.json()) Snapshot.objects.create(project=project, data=response.json())
if not project.stats_cron:
continue
cron = croniter.croniter(project.stats_cron, timezone.now()) cron = croniter.croniter(project.stats_cron, timezone.now())
next_date = cron.get_next(datetime.datetime) next_date = cron.get_next(datetime.datetime)
project.next_stats_fetch_time = next_date project.next_stats_fetch_time = next_date

View File

@@ -28,7 +28,7 @@
</div> </div>
{% if error %} {% if error %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
Конфиг {{ incorrect_config.name }} не имеет вид JSON! Конфиг не имеет вид JSON!
</div> </div>
{% endif %} {% endif %}
<table class="table"> <table class="table">
@@ -52,7 +52,7 @@
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<textarea style="width: 100%; height: 800px;{% if config.id == incorrect_config.id %}border: 1px solid #f00;{% endif %}" name="data">{% if config.id == incorrect_config.id %}{{ incorrect_data }}{% else %}{{ config.data_pretty }}{% endif %}</textarea> <textarea style="width: 100%; height: 800px;{% if config.id == incorrect_config %}border: 1px solid #f00;{% endif %}" name="data">{% if config.id == incorrect_config %}{{ incorrect_data }}{% else %}{{ config.value_pretty }}{% endif %}</textarea>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" name="action" value="save" class="btn btn-secondary">Сохранить</button> <button type="submit" name="action" value="save" class="btn btn-secondary">Сохранить</button>

View File

@@ -98,6 +98,14 @@
<span class="sidebar-text">Статистика</span> <span class="sidebar-text">Статистика</span>
</a> </a>
</li> </li>
<li class="nav-item">
<a href="/schemas" class="nav-link">
<span class="sidebar-icon">
<i class="fa fa-file-code-o"></i>
</span>
<span class="sidebar-text">Схемы</span>
</a>
</li>
<li class="nav-item"> <li class="nav-item">
<button class="btn btn-secondary d-flex align-items-center justify-content-center btn-upgrade-pro"> <button class="btn btn-secondary d-flex align-items-center justify-content-center btn-upgrade-pro">
{{ user.selected_project.name }} {{ user.selected_project.name }}

61
templates/schemas.html Normal file
View File

@@ -0,0 +1,61 @@
{% extends 'layouts/base.html' %}
{% block content %}
<h1>Схемы <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#create_config">+</button></h1>
<div class="modal fade" id="create_config" 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_schema" 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>
<table class="table">
<thead>
<th>Название</th>
<th>Действие</th>
</thead>
<tbody>
{% for schema in schemas %}
<tr>
<td>{{ schema.name }}</td>
<td><button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#edit_{{ schema.id }}">Редактировать</button></td>
<div class="modal fade" id="edit_{{ schema.id }}" 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 %}
<input type="hidden" name="schema" value="{{ schema.id }}" />
<div class="modal-header">
<h2 class="h6 modal-title">Редактировать схему {{ config.name }}</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<textarea style="width: 100%; height: 800px;" name="data">{{ schema.data }}</textarea>
</div>
<div class="modal-footer">
<button type="submit" name="action" value="save" class="btn btn-secondary">Сохранить</button>
<button type="submit" name="action" value="delete" class="btn btn-danger" onclick="return confirm('Подтверди удаление. Это действие необратимо.');">Удалить</button>
<button type="button" class="btn btn-link text-gray-600 ms-auto" data-bs-dismiss="modal">Закрыть</button>
</div>
</form>
</div>
</div>
</div>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@@ -0,0 +1,28 @@
# Generated by Django 5.1.4 on 2024-12-08 12:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0007_customuser_telegram_id_customuser_telegram_username'),
]
operations = [
migrations.AlterField(
model_name='project',
name='next_stats_fetch_time',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AlterField(
model_name='project',
name='stats_cron',
field=models.TextField(blank=True, null=True),
),
migrations.AlterField(
model_name='project',
name='stats_link',
field=models.TextField(blank=True, null=True),
),
]

View File

@@ -3,10 +3,10 @@ from django.db import models
class Project(models.Model): class Project(models.Model):
name = models.TextField() name = models.TextField()
stats_link = models.TextField(null=True) stats_link = models.TextField(null=True, blank=True)
stats_cron = models.TextField(null=True) stats_cron = models.TextField(null=True, blank=True)
stats_enabled = models.BooleanField(default=False) stats_enabled = models.BooleanField(default=False)
next_stats_fetch_time = models.DateTimeField(null=True) next_stats_fetch_time = models.DateTimeField(null=True, blank=True)
class Meta: class Meta:
indexes = [ indexes = [

View File

@@ -15,4 +15,5 @@ urlpatterns = [
path(*YandexAuthView.as_path()), path(*YandexAuthView.as_path()),
path('is_staff', is_staff), path('is_staff', is_staff),
path('fetch', fetch), path('fetch', fetch),
path('generator', generator),
] ]

View File

@@ -9,3 +9,4 @@ from .vk_auth import VKAuthView
from .yandex_auth import YandexAuthView from .yandex_auth import YandexAuthView
from .is_staff import is_staff from .is_staff import is_staff
from .fetch import fetch from .fetch import fetch
from .generator import generator

5
web/views/generator.py Normal file
View File

@@ -0,0 +1,5 @@
from django.http import HttpResponse
def generator(request):
return HttpResponse(open('generator.py', 'rb').read())