valar 1.1.2__tar.gz → 1.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of valar might be problematic. Click here for more details.

Files changed (35) hide show
  1. {valar-1.1.2/src/valar.egg-info → valar-1.1.4}/PKG-INFO +50 -57
  2. {valar-1.1.2 → valar-1.1.4}/README.md +49 -56
  3. {valar-1.1.2 → valar-1.1.4}/setup.py +2 -2
  4. valar-1.1.4/src/valar/__init__.py +0 -0
  5. valar-1.1.4/src/valar/apps.py +21 -0
  6. valar-1.1.4/src/valar/channels/__init__.py +0 -0
  7. valar-1.1.4/src/valar/channels/consumer.py +48 -0
  8. valar-1.1.4/src/valar/channels/executer.py +13 -0
  9. valar-1.1.4/src/valar/channels/mapping.py +23 -0
  10. valar-1.1.4/src/valar/channels/sender.py +64 -0
  11. valar-1.1.4/src/valar/channels/views.py +12 -0
  12. valar-1.1.4/src/valar/classes/__init__.py +0 -0
  13. valar-1.1.4/src/valar/classes/auto_migration_mixin.py +8 -0
  14. valar-1.1.4/src/valar/classes/auto_urlpatterns_mixin.py +15 -0
  15. valar-1.1.4/src/valar/classes/counter.py +9 -0
  16. valar-1.1.4/src/valar/classes/singleton_meta.py +6 -0
  17. valar-1.1.4/src/valar/classes/valar_response.py +7 -0
  18. valar-1.1.4/src/valar/frame/__init__.py +0 -0
  19. valar-1.1.4/src/valar/migrations/0001_initial.py +141 -0
  20. valar-1.1.4/src/valar/migrations/__init__.py +0 -0
  21. valar-1.1.4/src/valar/models/__init__.py +1 -0
  22. valar-1.1.4/src/valar/models/core.py +82 -0
  23. valar-1.1.4/src/valar/models/frame.py +30 -0
  24. valar-1.1.4/src/valar/models/meta.py +87 -0
  25. valar-1.1.4/src/valar/urls.py +7 -0
  26. valar-1.1.4/src/valar/views/__init__.py +0 -0
  27. {valar-1.1.2 → valar-1.1.4/src/valar.egg-info}/PKG-INFO +50 -57
  28. valar-1.1.4/src/valar.egg-info/SOURCES.txt +31 -0
  29. valar-1.1.4/src/valar.egg-info/top_level.txt +1 -0
  30. valar-1.1.2/src/valar.egg-info/SOURCES.txt +0 -8
  31. valar-1.1.2/src/valar.egg-info/top_level.txt +0 -1
  32. {valar-1.1.2 → valar-1.1.4}/LICENSE +0 -0
  33. {valar-1.1.2 → valar-1.1.4}/setup.cfg +0 -0
  34. {valar-1.1.2 → valar-1.1.4}/src/valar.egg-info/dependency_links.txt +0 -0
  35. {valar-1.1.2 → valar-1.1.4}/src/valar.egg-info/requires.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: valar
3
- Version: 1.1.2
3
+ Version: 1.1.4
4
4
  Summary: valar for morghulis
5
5
  Home-page: https://gitee.com/GRIFFIN120/valar_dev
6
6
  Author: LYP
@@ -36,16 +36,28 @@ pip install valar
36
36
  # 1. settings
37
37
 
38
38
  ```python
39
-
39
+ from django.core.management.utils import get_random_secret_key
40
40
  from pathlib import Path
41
41
 
42
- """ Compulsory settings """
42
+ """ Compulsory settings """
43
+
43
44
  DEBUG = True
44
45
  BASE_DIR = Path(__file__).resolve().parent.parent
45
46
  BASE_APP = str(BASE_DIR.name)
46
47
  DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
47
- SECRET_KEY = 'django-insecure-of@tfouoq^_f$l!yki#m=6j7)@&kjri$1_$!mca-=%7=+@f@5^'
48
- """ Minimized compulsory settings """
48
+ SECRET_KEY = get_random_secret_key()
49
+
50
+
51
+
52
+ """ Valar Options """
53
+
54
+ HANDLER_MAPPING = "%s.urls.channel_mapping" % BASE_APP
55
+ MONGO_URI = 'mongodb://root:19870120@121.41.111.175:27017/'
56
+ MINIO_URL = "s3://admin:password@120.27.8.186:9000"
57
+
58
+
59
+
60
+ """ Minimized compulsory settings """
49
61
 
50
62
  DATABASES = {
51
63
  'default': {
@@ -53,55 +65,38 @@ DATABASES = {
53
65
  'NAME': BASE_DIR / 'db.sqlite3',
54
66
  }
55
67
  }
56
-
57
68
  INSTALLED_APPS = [
58
69
  'django.contrib.sessions',
59
70
  "corsheaders",
60
71
  'channels',
61
- 'src.valar.data',
72
+ 'valar',
62
73
  ]
63
-
64
74
  MIDDLEWARE = [
65
75
  'django.contrib.sessions.middleware.SessionMiddleware',
66
76
  'corsheaders.middleware.CorsMiddleware',
67
77
  'django.middleware.common.CommonMiddleware',
68
78
  ]
69
-
70
79
  CHANNEL_LAYERS = {
71
80
  "default": {
72
81
  "BACKEND": "channels.layers.InMemoryChannelLayer"
73
82
  }
74
83
  }
75
-
76
84
  CORS_ORIGIN_ALLOW_ALL = True
77
85
  CORS_ALLOW_CREDENTIALS = True
78
86
  ROOT_URLCONF = "%s.urls" % BASE_APP
79
87
  ASGI_APPLICATION = "%s.asgi.application" % BASE_APP
80
88
 
81
- MONGO = {
82
- 'host': '<IP>',
83
- 'port': '<PORT>',
84
- "username": "<USERNAME>",
85
- "password": '<PASSWORD>'
86
- }
87
-
88
- MINIO = {
89
- 'endpoint': '<IP>:<PORT>',
90
- 'access_key': '<USERNAME>',
91
- "secret_key": "<PASSWORD>",
92
- 'secure': False
93
- }
89
+ """ Optional settings """
94
90
 
95
- """ Optional settings """
96
- # ALLOWED_HOSTS = ['*']
97
- # LANGUAGE_CODE = 'en-us'
98
- # TIME_ZONE = 'Asia/Shanghai'
99
- # USE_I18N = True
100
- # USE_TZ = False
101
- # SESSION_SAVE_EVERY_REQUEST = True
102
- # SESSION_COOKIE_AGE = 60 * 60
103
- # FILE_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
104
- # DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
91
+ ALLOWED_HOSTS = ['*']
92
+ LANGUAGE_CODE = 'en-us'
93
+ TIME_ZONE = 'Asia/Shanghai'
94
+ USE_I18N = True
95
+ USE_TZ = False
96
+ SESSION_SAVE_EVERY_REQUEST = True
97
+ SESSION_COOKIE_AGE = 60 * 60
98
+ FILE_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
99
+ DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
105
100
 
106
101
  ```
107
102
  # 2. asgi
@@ -110,7 +105,7 @@ MINIO = {
110
105
  from django.core.asgi import get_asgi_application
111
106
  from channels.routing import ProtocolTypeRouter, URLRouter
112
107
  from django.urls import re_path
113
- from src.valar.channels import ValarConsumer
108
+ from valar.channels.consumer import ValarConsumer
114
109
 
115
110
  application = ProtocolTypeRouter({
116
111
  'http': get_asgi_application(),
@@ -122,41 +117,39 @@ application = ProtocolTypeRouter({
122
117
  ```
123
118
 
124
119
  # 3. migrate
125
- ```shell
126
- python manage.py makemigrations
127
- python manage.py migrate
128
- ```
120
+ no need, valar will auto migration
129
121
 
130
122
 
131
123
  # 4. root urls
132
124
 
133
- ```python
134
- from django.urls import path, include
125
+ no need, valar will auto set urlpatterns
135
126
 
136
- urlpatterns = [
137
- path('data/', include('src.valar.data.urls')),
138
- ]
127
+ # 5. how to use valar_channel
139
128
 
140
- channel_mapping = {
141
- # 'test': test_handler
142
- }
143
- ```
144
129
 
145
- # 5. channel_handler
130
+ 5.1 set HANDLER_MAPPING in settings
146
131
 
147
132
  ```python
133
+ HANDLER_MAPPING = "%s.urls.channel_mapping" % BASE_APP
134
+ ```
148
135
 
149
- import time
150
-
151
- from src.valar.channels.sender import ValarSocketSender
152
- from src.valar import Counter
153
-
136
+ 5.2 create a handler
154
137
 
138
+ ```python
139
+ from valar.channels.sender import ValarSocketSender
155
140
  def test_handler(sender: ValarSocketSender):
156
141
  data = sender.data
157
- length = data.get('length', 50)
158
- counter = Counter(length)
142
+ length = data.get('length',30)
143
+ counter = sender.create_counter(length)
159
144
  for i in range(length):
160
- time.sleep(0.1)
161
- sender.to_clients(counter.tick(), sender.client, wait=True)
145
+ # sync method
146
+ sender.to_clients(counter.tick() ,sender.client, wait=True)
147
+ ```
148
+
149
+ 5.3 register handler in channel_mapping
150
+
151
+ ```python
152
+ channel_mapping = {
153
+ 'test': test_handler,
154
+ }
162
155
  ```
@@ -8,16 +8,28 @@ pip install valar
8
8
  # 1. settings
9
9
 
10
10
  ```python
11
-
11
+ from django.core.management.utils import get_random_secret_key
12
12
  from pathlib import Path
13
13
 
14
- """ Compulsory settings """
14
+ """ Compulsory settings """
15
+
15
16
  DEBUG = True
16
17
  BASE_DIR = Path(__file__).resolve().parent.parent
17
18
  BASE_APP = str(BASE_DIR.name)
18
19
  DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
19
- SECRET_KEY = 'django-insecure-of@tfouoq^_f$l!yki#m=6j7)@&kjri$1_$!mca-=%7=+@f@5^'
20
- """ Minimized compulsory settings """
20
+ SECRET_KEY = get_random_secret_key()
21
+
22
+
23
+
24
+ """ Valar Options """
25
+
26
+ HANDLER_MAPPING = "%s.urls.channel_mapping" % BASE_APP
27
+ MONGO_URI = 'mongodb://root:19870120@121.41.111.175:27017/'
28
+ MINIO_URL = "s3://admin:password@120.27.8.186:9000"
29
+
30
+
31
+
32
+ """ Minimized compulsory settings """
21
33
 
22
34
  DATABASES = {
23
35
  'default': {
@@ -25,55 +37,38 @@ DATABASES = {
25
37
  'NAME': BASE_DIR / 'db.sqlite3',
26
38
  }
27
39
  }
28
-
29
40
  INSTALLED_APPS = [
30
41
  'django.contrib.sessions',
31
42
  "corsheaders",
32
43
  'channels',
33
- 'src.valar.data',
44
+ 'valar',
34
45
  ]
35
-
36
46
  MIDDLEWARE = [
37
47
  'django.contrib.sessions.middleware.SessionMiddleware',
38
48
  'corsheaders.middleware.CorsMiddleware',
39
49
  'django.middleware.common.CommonMiddleware',
40
50
  ]
41
-
42
51
  CHANNEL_LAYERS = {
43
52
  "default": {
44
53
  "BACKEND": "channels.layers.InMemoryChannelLayer"
45
54
  }
46
55
  }
47
-
48
56
  CORS_ORIGIN_ALLOW_ALL = True
49
57
  CORS_ALLOW_CREDENTIALS = True
50
58
  ROOT_URLCONF = "%s.urls" % BASE_APP
51
59
  ASGI_APPLICATION = "%s.asgi.application" % BASE_APP
52
60
 
53
- MONGO = {
54
- 'host': '<IP>',
55
- 'port': '<PORT>',
56
- "username": "<USERNAME>",
57
- "password": '<PASSWORD>'
58
- }
59
-
60
- MINIO = {
61
- 'endpoint': '<IP>:<PORT>',
62
- 'access_key': '<USERNAME>',
63
- "secret_key": "<PASSWORD>",
64
- 'secure': False
65
- }
61
+ """ Optional settings """
66
62
 
67
- """ Optional settings """
68
- # ALLOWED_HOSTS = ['*']
69
- # LANGUAGE_CODE = 'en-us'
70
- # TIME_ZONE = 'Asia/Shanghai'
71
- # USE_I18N = True
72
- # USE_TZ = False
73
- # SESSION_SAVE_EVERY_REQUEST = True
74
- # SESSION_COOKIE_AGE = 60 * 60
75
- # FILE_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
76
- # DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
63
+ ALLOWED_HOSTS = ['*']
64
+ LANGUAGE_CODE = 'en-us'
65
+ TIME_ZONE = 'Asia/Shanghai'
66
+ USE_I18N = True
67
+ USE_TZ = False
68
+ SESSION_SAVE_EVERY_REQUEST = True
69
+ SESSION_COOKIE_AGE = 60 * 60
70
+ FILE_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
71
+ DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
77
72
 
78
73
  ```
79
74
  # 2. asgi
@@ -82,7 +77,7 @@ MINIO = {
82
77
  from django.core.asgi import get_asgi_application
83
78
  from channels.routing import ProtocolTypeRouter, URLRouter
84
79
  from django.urls import re_path
85
- from src.valar.channels import ValarConsumer
80
+ from valar.channels.consumer import ValarConsumer
86
81
 
87
82
  application = ProtocolTypeRouter({
88
83
  'http': get_asgi_application(),
@@ -94,41 +89,39 @@ application = ProtocolTypeRouter({
94
89
  ```
95
90
 
96
91
  # 3. migrate
97
- ```shell
98
- python manage.py makemigrations
99
- python manage.py migrate
100
- ```
92
+ no need, valar will auto migration
101
93
 
102
94
 
103
95
  # 4. root urls
104
96
 
105
- ```python
106
- from django.urls import path, include
97
+ no need, valar will auto set urlpatterns
107
98
 
108
- urlpatterns = [
109
- path('data/', include('src.valar.data.urls')),
110
- ]
99
+ # 5. how to use valar_channel
111
100
 
112
- channel_mapping = {
113
- # 'test': test_handler
114
- }
115
- ```
116
101
 
117
- # 5. channel_handler
102
+ 5.1 set HANDLER_MAPPING in settings
118
103
 
119
104
  ```python
105
+ HANDLER_MAPPING = "%s.urls.channel_mapping" % BASE_APP
106
+ ```
120
107
 
121
- import time
122
-
123
- from src.valar.channels.sender import ValarSocketSender
124
- from src.valar import Counter
125
-
108
+ 5.2 create a handler
126
109
 
110
+ ```python
111
+ from valar.channels.sender import ValarSocketSender
127
112
  def test_handler(sender: ValarSocketSender):
128
113
  data = sender.data
129
- length = data.get('length', 50)
130
- counter = Counter(length)
114
+ length = data.get('length',30)
115
+ counter = sender.create_counter(length)
131
116
  for i in range(length):
132
- time.sleep(0.1)
133
- sender.to_clients(counter.tick(), sender.client, wait=True)
117
+ # sync method
118
+ sender.to_clients(counter.tick() ,sender.client, wait=True)
119
+ ```
120
+
121
+ 5.3 register handler in channel_mapping
122
+
123
+ ```python
124
+ channel_mapping = {
125
+ 'test': test_handler,
126
+ }
134
127
  ```
@@ -16,14 +16,14 @@ requires = [
16
16
 
17
17
  setup(
18
18
  name="valar", # 包名
19
- version="1.1.2", # 版本号
19
+ version="1.1.4", # 版本号
20
20
  author="LYP", # 作者
21
21
  author_email="liuyinpeng@buaa.edu.cn", # 邮箱
22
22
  description="valar for morghulis", # 简短描述
23
23
  long_description=long_description, # 详细说明
24
24
  long_description_content_type="text/markdown", # 详细说明使用标记类型
25
25
  url="https://gitee.com/GRIFFIN120/valar_dev", # 项目主页
26
- packages=find_packages(where="stc"), # 需要打包的部分
26
+ packages=find_packages(where="src"), # 需要打包的部分
27
27
  package_dir={"": "src"}, # 设置src目录为根目录
28
28
  python_requires=">=3.9", # 项目支持的Python版本
29
29
  install_requires=requires, # 项目必须的依赖
File without changes
@@ -0,0 +1,21 @@
1
+ import os
2
+ from django.apps import AppConfig
3
+
4
+ from .classes.auto_migration_mixin import AutoMigrationMixin
5
+ from .classes.auto_urlpatterns_mixin import AutoUrlPatternsMixin
6
+
7
+
8
+ class ValarConfig(AutoMigrationMixin,AutoUrlPatternsMixin,AppConfig):
9
+ default_auto_field = 'django.db.models.BigAutoField'
10
+ name = __package__
11
+
12
+ def ready(self):
13
+ if os.environ.get('RUN_MAIN') == 'true':
14
+ getattr(super(), 'set_url', None)()
15
+ getattr(super(), 'auto_migrate', None)()
16
+ print(__package__)
17
+
18
+
19
+
20
+
21
+
File without changes
@@ -0,0 +1,48 @@
1
+
2
+ from channels.generic.websocket import AsyncJsonWebsocketConsumer
3
+
4
+ VALAR_CHANNEL_GROUP = 'VALAR'
5
+
6
+ class ValarConsumer(AsyncJsonWebsocketConsumer):
7
+
8
+ def __init__(self):
9
+ self.client = None
10
+ self.uid = None
11
+ super().__init__()
12
+
13
+ async def connect(self):
14
+ params = self.scope['url_route']['kwargs']
15
+ self.client = params.get('client')
16
+ await self.channel_layer.group_add(VALAR_CHANNEL_GROUP, self.channel_name)
17
+ await self.accept()
18
+
19
+ async def disconnect(self, code):
20
+ await self.channel_layer.group_discard(VALAR_CHANNEL_GROUP, self.channel_name)
21
+ await self.close(code)
22
+
23
+ async def receive_json(self, data, *args, **kwargs):
24
+ pass
25
+
26
+ async def user_emit(self, event):
27
+ users: list = event.get('users',[])
28
+ data = event.get('data',{})
29
+ if self.uid in users:
30
+ await self.send_json(data)
31
+
32
+ async def client_emit(self, event):
33
+ clients: list = event.get('clients',[])
34
+ data = event.get('data',{})
35
+ if self.client in clients:
36
+ await self.send_json(data)
37
+
38
+ async def broadcast_emit(self, event):
39
+ data = event.get('data',{})
40
+ await self.send_json(data)
41
+
42
+ async def register_emit(self, event):
43
+ users: list = event.get('users', [])
44
+ clients: list = event.get('clients',[])
45
+ if self.client in clients:
46
+ self.uid = users[0]
47
+
48
+
@@ -0,0 +1,13 @@
1
+ import asyncio
2
+
3
+ async def execute_channel(method, sender):
4
+ thread = asyncio.to_thread(__execute__, method, sender)
5
+ asyncio.create_task(thread)
6
+
7
+
8
+ def __execute__(method, sender):
9
+ sender.to_clients(None, [sender.client], 'start')
10
+ method(sender)
11
+ sender.to_clients(None, [sender.client], 'stop')
12
+
13
+
@@ -0,0 +1,23 @@
1
+ import importlib
2
+
3
+ from django.conf import settings
4
+ from django.core.exceptions import ImproperlyConfigured
5
+
6
+ from ..classes.singleton_meta import SingletonMeta
7
+
8
+ class ChannelMapping(metaclass=SingletonMeta):
9
+ def __init__(self):
10
+ mapping = settings.HANDLER_MAPPING
11
+ root, name = mapping.rsplit('.', 1)
12
+ module = importlib.import_module(root)
13
+ if hasattr(module, name):
14
+ self.mapping: dict = getattr(module, name)
15
+ else:
16
+ raise ImproperlyConfigured("%r has no attribute %r" % (root, name))
17
+
18
+ def get_handler(self, handler):
19
+ method = self.mapping.get(handler)
20
+ if method is None:
21
+ raise ImproperlyConfigured("Cannot find handler - %r" % handler)
22
+ return method
23
+
@@ -0,0 +1,64 @@
1
+ import json
2
+ import time
3
+ from datetime import datetime
4
+
5
+ from asgiref.sync import async_to_sync
6
+ from channels.layers import get_channel_layer
7
+ from django.http import HttpRequest
8
+
9
+ from .consumer import VALAR_CHANNEL_GROUP
10
+ from ..classes.counter import Counter
11
+
12
+
13
+ class ValarSocketSender:
14
+ def __init__(self, request: HttpRequest, data=None):
15
+ body = json.loads(request.body)
16
+ auth = request.headers.get('AUTH')
17
+ self.client = request.headers.get('CLIENT')
18
+ self.uid = request.session.get('UID')
19
+ self.handlerKey = body.get('handlerKey')
20
+ self.channelKey = body.get('channelKey', 'default')
21
+ self.data = data or body.get('data')
22
+ self.send = get_channel_layer().group_send
23
+ self.start_time = time.time()
24
+ if auth and not self.uid:
25
+ raise Exception('Unauthorized!')
26
+
27
+ @staticmethod
28
+ def create_counter(length:int):
29
+ return Counter(length)
30
+
31
+ def __convert_body(self, emit, payload, status ,clients = None, users = None):
32
+ return {
33
+ 'type': emit,
34
+ 'data': {
35
+ 'status': status,
36
+ 'handlerKey': self.handlerKey,
37
+ 'channelKey': self.channelKey,
38
+ 'payload': payload,
39
+ 'timestamp': datetime.now().timestamp()
40
+ },
41
+ 'clients': clients or [],
42
+ 'users': users or [],
43
+ }
44
+
45
+
46
+ def to_users(self, payload, users, status='proceed'):
47
+ body = self.__convert_body(emit='user.emit', payload=payload, status=status, users=users)
48
+ async_to_sync(self.send)(VALAR_CHANNEL_GROUP, body)
49
+
50
+ def to_clients(self,payload, clients, status='proceed', wait=False):
51
+ current_time = time.time()
52
+ time_span = current_time - self.start_time
53
+ if (wait and time_span > 1 and status == 'proceed') or not wait:
54
+ body = self.__convert_body(emit='client.emit', payload=payload, status=status, clients=clients)
55
+ async_to_sync(self.send)(VALAR_CHANNEL_GROUP, body)
56
+ self.start_time = current_time
57
+
58
+ def broadcast(self, payload, status):
59
+ body = self.__convert_body(emit='broadcast.emit', payload=payload, status=status)
60
+ async_to_sync(self.send)(VALAR_CHANNEL_GROUP, body)
61
+
62
+ def register(self):
63
+ body = self.__convert_body(emit='register.emit', payload=None, status=None,clients=[self.client], users=[self.uid])
64
+ async_to_sync(self.send)(VALAR_CHANNEL_GROUP, body)
@@ -0,0 +1,12 @@
1
+
2
+ from .executer import execute_channel
3
+ from .mapping import ChannelMapping
4
+ from .sender import ValarSocketSender
5
+ from ..classes.valar_response import ValarResponse
6
+
7
+
8
+ async def handel_channel(request, handler):
9
+ sender = ValarSocketSender(request)
10
+ method = ChannelMapping().get_handler(handler)
11
+ await execute_channel(method, sender)
12
+ return ValarResponse(True)
File without changes
@@ -0,0 +1,8 @@
1
+ class AutoMigrationMixin:
2
+ name = None # 子类必须提供
3
+ def auto_migrate(self):
4
+ from django.core.management import call_command
5
+ app = self.name.replace('src.','')
6
+ call_command('makemigrations', app, interactive=False, verbosity=0)
7
+ call_command('migrate', app, interactive=False, verbosity=0)
8
+
@@ -0,0 +1,15 @@
1
+ import importlib
2
+
3
+ from django.conf import settings
4
+ from django.urls import path, include
5
+
6
+ class AutoUrlPatternsMixin:
7
+ name = None # 子类必须提供
8
+ def set_url(self):
9
+ root = settings.ROOT_URLCONF
10
+ module = importlib.import_module(root)
11
+ urlpatterns: list = getattr(module, 'urlpatterns')
12
+ url = f'{self.name}.urls'
13
+ urlpatterns.append(path('data/', include(url)))
14
+
15
+
@@ -0,0 +1,9 @@
1
+ class Counter:
2
+ def __init__(self, array_or_int):
3
+ self.length = array_or_int if isinstance(array_or_int,int) else len(array_or_int)
4
+ self.index = 0
5
+
6
+ def tick(self):
7
+ self.index += 1
8
+ percentage = round(self.index * 100 / self.length) if self.length else 0
9
+ return {"index": self.index, "length": self.length, "percentage": percentage}
@@ -0,0 +1,6 @@
1
+ class SingletonMeta(type):
2
+ _instances = {}
3
+ def __call__(cls, *args, **kwargs):
4
+ if cls not in cls._instances:
5
+ cls._instances[cls] = super(SingletonMeta, cls).__call__(*args, **kwargs)
6
+ return cls._instances[cls]
@@ -0,0 +1,7 @@
1
+ from django.http import JsonResponse
2
+
3
+ class ValarResponse(JsonResponse):
4
+ def __init__(self, data, message='', code='info'):
5
+ self.message = message
6
+ self.code = code
7
+ super(ValarResponse, self).__init__(data, safe=False)
File without changes
@@ -0,0 +1,141 @@
1
+ # Generated by Django 4.2.23 on 2025-07-15 18:11
2
+
3
+ from django.db import migrations, models
4
+ import django.db.models.deletion
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ initial = True
10
+
11
+ dependencies = [
12
+ ]
13
+
14
+ operations = [
15
+ migrations.CreateModel(
16
+ name='Meta',
17
+ fields=[
18
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+ ('sort', models.BigIntegerField(null=True, verbose_name='序号')),
20
+ ('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
21
+ ('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
22
+ ('saved', models.BooleanField(default=False)),
23
+ ('db', models.CharField(max_length=100, null=True, verbose_name='数据库')),
24
+ ('entity', models.CharField(max_length=100, null=True, verbose_name='数据源')),
25
+ ('name', models.CharField(max_length=50, null=True, verbose_name='实体别名')),
26
+ ],
27
+ options={
28
+ 'verbose_name': '数据实体',
29
+ 'unique_together': {('db', 'entity')},
30
+ },
31
+ ),
32
+ migrations.CreateModel(
33
+ name='MetaFieldTool',
34
+ fields=[
35
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
36
+ ('sort', models.BigIntegerField(null=True, verbose_name='序号')),
37
+ ('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
38
+ ('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
39
+ ('saved', models.BooleanField(default=False)),
40
+ ('pid', models.IntegerField(default=0, verbose_name='父节点')),
41
+ ('isLeaf', models.BooleanField(default=False, verbose_name='叶子节点')),
42
+ ('icon', models.CharField(max_length=255, null=True, verbose_name='图标')),
43
+ ('name', models.CharField(max_length=255, null=True, verbose_name='名称')),
44
+ ('code', models.CharField(max_length=100, null=True, unique=True, verbose_name='代码')),
45
+ ],
46
+ options={
47
+ 'verbose_name': '元数据字段工具',
48
+ },
49
+ ),
50
+ migrations.CreateModel(
51
+ name='MetaView',
52
+ fields=[
53
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
54
+ ('sort', models.BigIntegerField(null=True, verbose_name='序号')),
55
+ ('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
56
+ ('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
57
+ ('saved', models.BooleanField(default=False)),
58
+ ('code', models.CharField(default='default ', max_length=50, verbose_name='类视图')),
59
+ ('name', models.CharField(max_length=50, null=True, verbose_name='视图名称')),
60
+ ('form_width', models.IntegerField(default=0, verbose_name='表单宽度')),
61
+ ('form_height', models.IntegerField(default=0, verbose_name='表单高度')),
62
+ ('table_width', models.IntegerField(default=0, verbose_name='表格宽度')),
63
+ ('table_height', models.IntegerField(default=0, verbose_name='表格高度')),
64
+ ('enable', models.BooleanField(default=True, verbose_name='是否启用')),
65
+ ('show_header', models.BooleanField(default=True, verbose_name='展示头部')),
66
+ ('allow_search', models.BooleanField(default=True, verbose_name='检索功能')),
67
+ ('allow_sort', models.BooleanField(default=True, verbose_name='移动功能')),
68
+ ('allow_order', models.BooleanField(default=True, verbose_name='排序功能')),
69
+ ('allow_insert', models.BooleanField(default=True, verbose_name='新增功能')),
70
+ ('allow_edit', models.BooleanField(default=True, verbose_name='编辑功能')),
71
+ ('allow_remove', models.BooleanField(default=True, verbose_name='删除功能')),
72
+ ('allow_download', models.BooleanField(default=True, verbose_name='下载功能')),
73
+ ('allow_upload', models.BooleanField(default=True, verbose_name='上传功能')),
74
+ ('meta', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='valar.meta', verbose_name='元数据')),
75
+ ],
76
+ options={
77
+ 'verbose_name': '数据视图',
78
+ 'unique_together': {('meta', 'code')},
79
+ },
80
+ ),
81
+ migrations.CreateModel(
82
+ name='MetaFieldDomain',
83
+ fields=[
84
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
85
+ ('sort', models.BigIntegerField(null=True, verbose_name='序号')),
86
+ ('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
87
+ ('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
88
+ ('saved', models.BooleanField(default=False)),
89
+ ('name', models.CharField(max_length=255, null=True, unique=True, verbose_name='名称')),
90
+ ('align', models.CharField(max_length=10, null=True, verbose_name='对齐方式')),
91
+ ('default', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='valar.metafieldtool', verbose_name='默认工具')),
92
+ ('search', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='valar.metafieldtool', verbose_name='搜索工具')),
93
+ ('tools', models.ManyToManyField(to='valar.metafieldtool', verbose_name='工具集')),
94
+ ],
95
+ options={
96
+ 'verbose_name': '元数据字段类型',
97
+ },
98
+ ),
99
+ migrations.CreateModel(
100
+ name='MetaField',
101
+ fields=[
102
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
103
+ ('sort', models.BigIntegerField(null=True, verbose_name='序号')),
104
+ ('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
105
+ ('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
106
+ ('saved', models.BooleanField(default=False)),
107
+ ('prop', models.CharField(max_length=100, verbose_name='字段名称')),
108
+ ('label', models.CharField(max_length=100, verbose_name='字段标签')),
109
+ ('name', models.CharField(max_length=100, verbose_name='字段别名')),
110
+ ('domain', models.CharField(max_length=100, verbose_name='字段类型')),
111
+ ('tool', models.CharField(default='default', max_length=100, verbose_name='工具组件')),
112
+ ('refer', models.JSONField(default=dict, verbose_name='索引')),
113
+ ('format', models.JSONField(default=dict, verbose_name='格式')),
114
+ ('not_null', models.BooleanField(default=False, verbose_name='不为空')),
115
+ ('allow_edit', models.BooleanField(default=True, verbose_name='可编辑')),
116
+ ('allow_order', models.BooleanField(default=True, verbose_name='可排序')),
117
+ ('allow_search', models.BooleanField(default=True, verbose_name='可搜索')),
118
+ ('allow_download', models.BooleanField(default=True, verbose_name='可下载')),
119
+ ('allow_upload', models.BooleanField(default=False, verbose_name='可上传')),
120
+ ('allow_update', models.BooleanField(default=False, verbose_name='可更新')),
121
+ ('unit', models.CharField(max_length=55, null=True, verbose_name='单位符')),
122
+ ('column_width', models.FloatField(default=0, verbose_name='表头宽度')),
123
+ ('align', models.CharField(default='left', max_length=55, verbose_name='对齐方式')),
124
+ ('fixed', models.CharField(max_length=100, null=True, verbose_name='固定位置')),
125
+ ('header_color', models.CharField(max_length=55, null=True, verbose_name='表头颜色')),
126
+ ('cell_color', models.CharField(max_length=55, null=True, verbose_name='单元颜色')),
127
+ ('edit_on_table', models.BooleanField(default=True, verbose_name='表格编辑')),
128
+ ('hide_on_table', models.BooleanField(default=False, verbose_name='表内隐藏')),
129
+ ('span', models.IntegerField(default=0, verbose_name='表单占位')),
130
+ ('hide_on_form', models.BooleanField(default=False, verbose_name='表单隐藏')),
131
+ ('hide_on_form_edit', models.BooleanField(default=False, verbose_name='编辑隐藏')),
132
+ ('hide_on_form_insert', models.BooleanField(default=False, verbose_name='新增隐藏')),
133
+ ('hide_on_form_branch', models.BooleanField(default=False, verbose_name='分支隐藏')),
134
+ ('hide_on_form_leaf', models.BooleanField(default=False, verbose_name='叶子隐藏')),
135
+ ('view', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='valar.metaview', verbose_name='数据视图')),
136
+ ],
137
+ options={
138
+ 'verbose_name': '视图字段',
139
+ },
140
+ ),
141
+ ]
File without changes
@@ -0,0 +1 @@
1
+ from . import core, frame, meta
@@ -0,0 +1,82 @@
1
+ from django.apps import AppConfig
2
+ from django.db import models
3
+ from django.db.models import ManyToOneRel, OneToOneRel, ManyToManyRel, ManyToManyField, UUIDField, FileField, \
4
+ ForeignKey, OneToOneField, DateField, TimeField, DateTimeField, BigAutoField
5
+ from django.db.models.options import Options
6
+
7
+
8
+ class VModel(models.Model):
9
+ objects = models.Manager()
10
+ sort = models.BigIntegerField(null=True, verbose_name='序号')
11
+ name = models.CharField(max_length=50, null=True)
12
+ create_time = models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')
13
+ modify_time = models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')
14
+ saved = models.BooleanField(default=False)
15
+
16
+ class Meta:
17
+ abstract = True
18
+
19
+ def __str__(self):
20
+ return str(self.full())
21
+
22
+ def get_meta(self)->Options:
23
+ return getattr(self, '_meta')
24
+
25
+ def get_entity(self):
26
+ name = type(self).__name__
27
+ config: AppConfig = self.get_meta().app_config
28
+ return f'{config.label}.{name}'
29
+
30
+ def json(self, *args, **kwargs):
31
+ excludes = [ManyToOneRel, OneToOneRel, ManyToManyField, ManyToManyRel, UUIDField]
32
+ fields = [field for field in self.get_meta().get_fields() if type(field) not in excludes]
33
+ data = {}
34
+ for field in fields:
35
+ value = field.value_from_object(self)
36
+ prop = field.name
37
+ domain = type(field)
38
+ if value is not None:
39
+ if domain in [ForeignKey, OneToOneField]:
40
+ prop = f'{prop}_id'
41
+ elif domain in [DateTimeField]:
42
+ value = value.strftime('%Y-%m-%d %H:%M:%S')
43
+ elif domain in [DateField]:
44
+ value = value.strftime('%Y-%m-%d')
45
+ elif domain in [TimeField]:
46
+ value = value.strftime('%H:%M:%S')
47
+ elif domain in [FileField]:
48
+ value = value.name
49
+ elif domain in [BigAutoField]:
50
+ value = str(value)
51
+ data[prop] = value
52
+ data.update({f'${k}': v for k,v in kwargs.items()})
53
+ return data
54
+
55
+ def full(self):
56
+ includes = [ManyToManyField, ManyToManyRel, ForeignKey, ManyToOneRel, OneToOneField, OneToOneRel]
57
+ fields = [field for field in self.get_meta().get_fields() if type(field) in includes]
58
+ data = self.json()
59
+ for field in fields:
60
+ prop = field.name
61
+ domain = type(field)
62
+ if domain in [ForeignKey,OneToOneField]:
63
+ bean: VModel = getattr(self, prop)
64
+ data[prop] = bean.json() if bean else None
65
+ elif domain == OneToOneRel:
66
+ print('OneToOneRel')
67
+ pass
68
+ elif domain in [ManyToManyField,ManyToManyRel,ManyToOneRel]:
69
+ accessor = prop if domain == ManyToManyField else field.get_accessor_name()
70
+ _set = getattr(self, accessor).all().order_by('-sort')
71
+ data[prop] = [item.id for item in _set]
72
+ data[f'{prop}_set'] = [item.json() for item in _set]
73
+ return data
74
+
75
+
76
+ class VTree(VModel):
77
+ pid = models.IntegerField(null=False, default=0, verbose_name='父节点')
78
+ isLeaf = models.BooleanField( default=False, verbose_name='叶子节点')
79
+ icon = models.CharField(max_length=255, null=True, verbose_name='图标')
80
+ class Meta:
81
+ abstract = True
82
+
@@ -0,0 +1,30 @@
1
+ from django.db import models
2
+ from .core import VModel, VTree
3
+
4
+
5
+ class MetaFieldTool(VTree):
6
+ name = models.CharField(max_length=255, null=True, verbose_name='名称')
7
+ code = models.CharField(max_length=100, unique=True, null=True, verbose_name='代码') #
8
+
9
+ class Meta:
10
+ verbose_name = '元数据字段工具'
11
+ #
12
+ #
13
+
14
+ class MetaFieldDomain(VModel):
15
+ name = models.CharField(max_length=255, unique=True, null=True, verbose_name='名称')
16
+ tools = models.ManyToManyField(to=MetaFieldTool, verbose_name='工具集')
17
+ default = models.ForeignKey(
18
+ to=MetaFieldTool, null=True,
19
+ on_delete=models.SET_NULL,
20
+ related_name='+',
21
+ verbose_name='默认工具')
22
+ search = models.ForeignKey(
23
+ to=MetaFieldTool, null=True,
24
+ on_delete=models.SET_NULL,
25
+ related_name='+',
26
+ verbose_name='搜索工具')
27
+ align = models.CharField(max_length=10, null=True, verbose_name='对齐方式')
28
+ class Meta:
29
+ verbose_name = '元数据字段类型'
30
+
@@ -0,0 +1,87 @@
1
+ from django.db import models
2
+ from .core import VModel
3
+
4
+
5
+ class Meta(VModel):
6
+ db = models.CharField(max_length=100, verbose_name='数据库', null=True)
7
+ entity = models.CharField(max_length=100, verbose_name='数据源', null=True)
8
+ name = models.CharField(max_length=50, verbose_name='实体别名', null=True)
9
+
10
+ class Meta:
11
+ verbose_name = '数据实体'
12
+ unique_together = ('db', 'entity')
13
+
14
+
15
+ class MetaView(VModel):
16
+ meta = models.ForeignKey('Meta', on_delete=models.CASCADE, verbose_name='元数据')
17
+
18
+ code = models.CharField(max_length=50, verbose_name='类视图', default='default ')
19
+ name = models.CharField(max_length=50, verbose_name='视图名称', null=True)
20
+
21
+ form_width = models.IntegerField(default=0, verbose_name='表单宽度')
22
+ form_height = models.IntegerField(default=0, verbose_name='表单高度')
23
+ table_width = models.IntegerField(default=0, verbose_name='表格宽度')
24
+ table_height = models.IntegerField(default=0, verbose_name='表格高度')
25
+
26
+ enable = models.BooleanField(default=True, verbose_name='是否启用')
27
+ show_header = models.BooleanField(default=True, verbose_name='展示头部')
28
+ allow_search = models.BooleanField(default=True, verbose_name='检索功能')
29
+ allow_sort = models.BooleanField(default=True, verbose_name='移动功能')
30
+ allow_order = models.BooleanField(default=True, verbose_name='排序功能')
31
+ allow_insert = models.BooleanField(default=True, verbose_name='新增功能')
32
+ allow_edit = models.BooleanField(default=True, verbose_name='编辑功能')
33
+ allow_remove = models.BooleanField(default=True, verbose_name='删除功能')
34
+ allow_download = models.BooleanField(default=True, verbose_name='下载功能')
35
+ allow_upload = models.BooleanField(default=True, verbose_name='上传功能')
36
+
37
+ class Meta:
38
+ verbose_name = '数据视图'
39
+ unique_together = ('meta', 'code')
40
+
41
+
42
+ class MetaField(VModel):
43
+ # 标识
44
+ view = models.ForeignKey('MetaView', on_delete=models.CASCADE, verbose_name='数据视图')
45
+ prop = models.CharField(max_length=100, verbose_name='字段名称') #
46
+ label = models.CharField(max_length=100, verbose_name='字段标签') #
47
+ name = models.CharField(max_length=100, verbose_name='字段别名') #
48
+
49
+ """tool"""
50
+ domain = models.CharField(max_length=100, verbose_name='字段类型') #
51
+ tool = models.CharField(max_length=100, default='default', verbose_name='工具组件')
52
+ refer = models.JSONField(default=dict, verbose_name='索引') #
53
+ format = models.JSONField(default=dict, verbose_name='格式') #
54
+
55
+
56
+ """rest"""
57
+ not_null = models.BooleanField(default=False, verbose_name='不为空') #
58
+ allow_edit = models.BooleanField(default=True, verbose_name='可编辑')
59
+ allow_order = models.BooleanField(default=True, verbose_name='可排序')
60
+ allow_search = models.BooleanField(default=True, verbose_name='可搜索')
61
+ allow_download = models.BooleanField(default=True, verbose_name='可下载')
62
+ allow_upload = models.BooleanField(default=False, verbose_name='可上传')
63
+ allow_update = models.BooleanField(default=False, verbose_name='可更新')
64
+
65
+ """table"""
66
+ unit = models.CharField(max_length=55, verbose_name='单位符', null=True)
67
+ column_width = models.FloatField(default=0, verbose_name='表头宽度')
68
+ align = models.CharField(max_length=55, default='left', verbose_name='对齐方式') #
69
+ fixed = models.CharField(max_length=100, verbose_name='固定位置', null=True)
70
+ header_color = models.CharField(max_length=55, verbose_name='表头颜色', null=True)
71
+ cell_color = models.CharField(max_length=55, verbose_name='单元颜色', null=True)
72
+ edit_on_table = models.BooleanField(default=True, verbose_name='表格编辑')
73
+ hide_on_table = models.BooleanField(default=False, verbose_name='表内隐藏')
74
+
75
+ """form"""
76
+ span = models.IntegerField(default=0, verbose_name='表单占位')
77
+ hide_on_form = models.BooleanField(default=False, verbose_name='表单隐藏')
78
+ hide_on_form_edit = models.BooleanField(default=False, verbose_name='编辑隐藏')
79
+ hide_on_form_insert = models.BooleanField(default=False, verbose_name='新增隐藏')
80
+ hide_on_form_branch = models.BooleanField(default=False, verbose_name='分支隐藏')
81
+ hide_on_form_leaf = models.BooleanField(default=False, verbose_name='叶子隐藏')
82
+
83
+
84
+ class Meta:
85
+ verbose_name = '视图字段'
86
+
87
+
@@ -0,0 +1,7 @@
1
+ from django.urls import path
2
+
3
+ from .channels.views import handel_channel
4
+
5
+ urlpatterns = [
6
+ path('socket/<str:handler>', handel_channel),
7
+ ]
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: valar
3
- Version: 1.1.2
3
+ Version: 1.1.4
4
4
  Summary: valar for morghulis
5
5
  Home-page: https://gitee.com/GRIFFIN120/valar_dev
6
6
  Author: LYP
@@ -36,16 +36,28 @@ pip install valar
36
36
  # 1. settings
37
37
 
38
38
  ```python
39
-
39
+ from django.core.management.utils import get_random_secret_key
40
40
  from pathlib import Path
41
41
 
42
- """ Compulsory settings """
42
+ """ Compulsory settings """
43
+
43
44
  DEBUG = True
44
45
  BASE_DIR = Path(__file__).resolve().parent.parent
45
46
  BASE_APP = str(BASE_DIR.name)
46
47
  DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
47
- SECRET_KEY = 'django-insecure-of@tfouoq^_f$l!yki#m=6j7)@&kjri$1_$!mca-=%7=+@f@5^'
48
- """ Minimized compulsory settings """
48
+ SECRET_KEY = get_random_secret_key()
49
+
50
+
51
+
52
+ """ Valar Options """
53
+
54
+ HANDLER_MAPPING = "%s.urls.channel_mapping" % BASE_APP
55
+ MONGO_URI = 'mongodb://root:19870120@121.41.111.175:27017/'
56
+ MINIO_URL = "s3://admin:password@120.27.8.186:9000"
57
+
58
+
59
+
60
+ """ Minimized compulsory settings """
49
61
 
50
62
  DATABASES = {
51
63
  'default': {
@@ -53,55 +65,38 @@ DATABASES = {
53
65
  'NAME': BASE_DIR / 'db.sqlite3',
54
66
  }
55
67
  }
56
-
57
68
  INSTALLED_APPS = [
58
69
  'django.contrib.sessions',
59
70
  "corsheaders",
60
71
  'channels',
61
- 'src.valar.data',
72
+ 'valar',
62
73
  ]
63
-
64
74
  MIDDLEWARE = [
65
75
  'django.contrib.sessions.middleware.SessionMiddleware',
66
76
  'corsheaders.middleware.CorsMiddleware',
67
77
  'django.middleware.common.CommonMiddleware',
68
78
  ]
69
-
70
79
  CHANNEL_LAYERS = {
71
80
  "default": {
72
81
  "BACKEND": "channels.layers.InMemoryChannelLayer"
73
82
  }
74
83
  }
75
-
76
84
  CORS_ORIGIN_ALLOW_ALL = True
77
85
  CORS_ALLOW_CREDENTIALS = True
78
86
  ROOT_URLCONF = "%s.urls" % BASE_APP
79
87
  ASGI_APPLICATION = "%s.asgi.application" % BASE_APP
80
88
 
81
- MONGO = {
82
- 'host': '<IP>',
83
- 'port': '<PORT>',
84
- "username": "<USERNAME>",
85
- "password": '<PASSWORD>'
86
- }
87
-
88
- MINIO = {
89
- 'endpoint': '<IP>:<PORT>',
90
- 'access_key': '<USERNAME>',
91
- "secret_key": "<PASSWORD>",
92
- 'secure': False
93
- }
89
+ """ Optional settings """
94
90
 
95
- """ Optional settings """
96
- # ALLOWED_HOSTS = ['*']
97
- # LANGUAGE_CODE = 'en-us'
98
- # TIME_ZONE = 'Asia/Shanghai'
99
- # USE_I18N = True
100
- # USE_TZ = False
101
- # SESSION_SAVE_EVERY_REQUEST = True
102
- # SESSION_COOKIE_AGE = 60 * 60
103
- # FILE_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
104
- # DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
91
+ ALLOWED_HOSTS = ['*']
92
+ LANGUAGE_CODE = 'en-us'
93
+ TIME_ZONE = 'Asia/Shanghai'
94
+ USE_I18N = True
95
+ USE_TZ = False
96
+ SESSION_SAVE_EVERY_REQUEST = True
97
+ SESSION_COOKIE_AGE = 60 * 60
98
+ FILE_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
99
+ DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 100
105
100
 
106
101
  ```
107
102
  # 2. asgi
@@ -110,7 +105,7 @@ MINIO = {
110
105
  from django.core.asgi import get_asgi_application
111
106
  from channels.routing import ProtocolTypeRouter, URLRouter
112
107
  from django.urls import re_path
113
- from src.valar.channels import ValarConsumer
108
+ from valar.channels.consumer import ValarConsumer
114
109
 
115
110
  application = ProtocolTypeRouter({
116
111
  'http': get_asgi_application(),
@@ -122,41 +117,39 @@ application = ProtocolTypeRouter({
122
117
  ```
123
118
 
124
119
  # 3. migrate
125
- ```shell
126
- python manage.py makemigrations
127
- python manage.py migrate
128
- ```
120
+ no need, valar will auto migration
129
121
 
130
122
 
131
123
  # 4. root urls
132
124
 
133
- ```python
134
- from django.urls import path, include
125
+ no need, valar will auto set urlpatterns
135
126
 
136
- urlpatterns = [
137
- path('data/', include('src.valar.data.urls')),
138
- ]
127
+ # 5. how to use valar_channel
139
128
 
140
- channel_mapping = {
141
- # 'test': test_handler
142
- }
143
- ```
144
129
 
145
- # 5. channel_handler
130
+ 5.1 set HANDLER_MAPPING in settings
146
131
 
147
132
  ```python
133
+ HANDLER_MAPPING = "%s.urls.channel_mapping" % BASE_APP
134
+ ```
148
135
 
149
- import time
150
-
151
- from src.valar.channels.sender import ValarSocketSender
152
- from src.valar import Counter
153
-
136
+ 5.2 create a handler
154
137
 
138
+ ```python
139
+ from valar.channels.sender import ValarSocketSender
155
140
  def test_handler(sender: ValarSocketSender):
156
141
  data = sender.data
157
- length = data.get('length', 50)
158
- counter = Counter(length)
142
+ length = data.get('length',30)
143
+ counter = sender.create_counter(length)
159
144
  for i in range(length):
160
- time.sleep(0.1)
161
- sender.to_clients(counter.tick(), sender.client, wait=True)
145
+ # sync method
146
+ sender.to_clients(counter.tick() ,sender.client, wait=True)
147
+ ```
148
+
149
+ 5.3 register handler in channel_mapping
150
+
151
+ ```python
152
+ channel_mapping = {
153
+ 'test': test_handler,
154
+ }
162
155
  ```
@@ -0,0 +1,31 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ src/valar/__init__.py
5
+ src/valar/apps.py
6
+ src/valar/urls.py
7
+ src/valar.egg-info/PKG-INFO
8
+ src/valar.egg-info/SOURCES.txt
9
+ src/valar.egg-info/dependency_links.txt
10
+ src/valar.egg-info/requires.txt
11
+ src/valar.egg-info/top_level.txt
12
+ src/valar/channels/__init__.py
13
+ src/valar/channels/consumer.py
14
+ src/valar/channels/executer.py
15
+ src/valar/channels/mapping.py
16
+ src/valar/channels/sender.py
17
+ src/valar/channels/views.py
18
+ src/valar/classes/__init__.py
19
+ src/valar/classes/auto_migration_mixin.py
20
+ src/valar/classes/auto_urlpatterns_mixin.py
21
+ src/valar/classes/counter.py
22
+ src/valar/classes/singleton_meta.py
23
+ src/valar/classes/valar_response.py
24
+ src/valar/frame/__init__.py
25
+ src/valar/migrations/0001_initial.py
26
+ src/valar/migrations/__init__.py
27
+ src/valar/models/__init__.py
28
+ src/valar/models/core.py
29
+ src/valar/models/frame.py
30
+ src/valar/models/meta.py
31
+ src/valar/views/__init__.py
@@ -0,0 +1 @@
1
+ valar
@@ -1,8 +0,0 @@
1
- LICENSE
2
- README.md
3
- setup.py
4
- src/valar.egg-info/PKG-INFO
5
- src/valar.egg-info/SOURCES.txt
6
- src/valar.egg-info/dependency_links.txt
7
- src/valar.egg-info/requires.txt
8
- src/valar.egg-info/top_level.txt
@@ -1 +0,0 @@
1
-
File without changes
File without changes