valar 1.1.3__py3-none-any.whl → 1.2.0__py3-none-any.whl

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 (41) hide show
  1. valar/apps.py +8 -10
  2. valar/channels/consumer.py +10 -12
  3. valar/channels/executer.py +7 -6
  4. valar/channels/sender.py +95 -43
  5. valar/channels/views.py +2 -3
  6. valar/classes/app_mixins/auto_migration_mixin.py +8 -0
  7. valar/classes/{auto_urlpatterns_mixin.py → app_mixins/auto_urlpatterns_mixin.py} +3 -3
  8. valar/classes/valar_minio.py +80 -0
  9. valar/classes/valar_response.py +2 -1
  10. valar/dao/__init__.py +45 -0
  11. valar/dao/abstract.py +106 -0
  12. valar/dao/defaults/__init__.py +0 -0
  13. valar/dao/defaults/field_keys_default.py +48 -0
  14. valar/dao/defaults/field_values_default.py +135 -0
  15. valar/dao/defaults/view_defaults.py +26 -0
  16. valar/dao/engine.py +80 -0
  17. valar/dao/frame.py +189 -0
  18. valar/dao/meta.py +128 -0
  19. valar/dao/mon_dao.py +113 -0
  20. valar/dao/mon_field.py +23 -0
  21. valar/dao/orm_dao.py +304 -0
  22. valar/dao/orm_field.py +142 -0
  23. valar/dao/query.py +36 -0
  24. valar/migrations/0001_initial.py +149 -0
  25. valar/models/core.py +16 -10
  26. valar/models/frame.py +4 -7
  27. valar/models/meta.py +19 -16
  28. valar/urls.py +22 -0
  29. valar/views/file.py +49 -0
  30. valar/views/handler.py +33 -0
  31. valar/views/meta.py +150 -0
  32. valar/views/rest.py +90 -0
  33. {valar-1.1.3.dist-info → valar-1.2.0.dist-info}/METADATA +50 -57
  34. valar-1.2.0.dist-info/RECORD +47 -0
  35. valar/classes/auto_migration_mixin.py +0 -7
  36. valar-1.1.3.dist-info/RECORD +0 -27
  37. /valar/{classes → channels}/counter.py +0 -0
  38. /valar/{frame → classes/app_mixins}/__init__.py +0 -0
  39. {valar-1.1.3.dist-info → valar-1.2.0.dist-info}/WHEEL +0 -0
  40. {valar-1.1.3.dist-info → valar-1.2.0.dist-info}/licenses/LICENSE +0 -0
  41. {valar-1.1.3.dist-info → valar-1.2.0.dist-info}/top_level.txt +0 -0
valar/apps.py CHANGED
@@ -1,20 +1,18 @@
1
1
  import os
2
2
  from django.apps import AppConfig
3
+ from .classes.app_mixins.auto_migration_mixin import AutoMigrationMixin
4
+ from .classes.app_mixins.auto_urlpatterns_mixin import AutoUrlPatternsMixin
3
5
 
4
- from .classes.auto_migration_mixin import AutoMigrationMixin
5
- from .classes.auto_urlpatterns_mixin import AutoUrlPatternsMixin
6
+ valar_app = __package__.replace('src.', '')
6
7
 
7
8
 
8
- class ValarConfig(AutoMigrationMixin,AutoUrlPatternsMixin,AppConfig):
9
+ class ValarConfig(AutoMigrationMixin, AutoUrlPatternsMixin, AppConfig):
9
10
  default_auto_field = 'django.db.models.BigAutoField'
10
- name = 'valar'
11
+ name = __package__
12
+
11
13
  def ready(self):
12
14
  if os.environ.get('RUN_MAIN') == 'true':
15
+ from .dao.frame import MetaFrame
13
16
  getattr(super(), 'set_url', None)()
14
17
  getattr(super(), 'auto_migrate', None)()
15
- print('valar')
16
-
17
-
18
-
19
-
20
-
18
+ MetaFrame()
@@ -1,8 +1,8 @@
1
-
2
1
  from channels.generic.websocket import AsyncJsonWebsocketConsumer
3
2
 
4
3
  VALAR_CHANNEL_GROUP = 'VALAR'
5
4
 
5
+
6
6
  class ValarConsumer(AsyncJsonWebsocketConsumer):
7
7
 
8
8
  def __init__(self):
@@ -24,25 +24,23 @@ class ValarConsumer(AsyncJsonWebsocketConsumer):
24
24
  pass
25
25
 
26
26
  async def user_emit(self, event):
27
- users: list = event.get('users',[])
28
- data = event.get('data',{})
27
+ users: list = event.get('users', [])
28
+ data = event.get('data', {})
29
29
  if self.uid in users:
30
30
  await self.send_json(data)
31
31
 
32
32
  async def client_emit(self, event):
33
- clients: list = event.get('clients',[])
34
- data = event.get('data',{})
33
+ clients: list = event.get('clients', [])
34
+ data = event.get('data', {})
35
35
  if self.client in clients:
36
36
  await self.send_json(data)
37
37
 
38
38
  async def broadcast_emit(self, event):
39
- data = event.get('data',{})
39
+ data = event.get('data', {})
40
40
  await self.send_json(data)
41
41
 
42
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
-
43
+ uid = event.get('uid', )
44
+ client = event.get('client')
45
+ if self.client == client:
46
+ self.uid = uid
@@ -1,13 +1,14 @@
1
1
  import asyncio
2
2
 
3
- async def execute_channel(method, sender):
3
+ from ..channels.sender import ValarChannelSender
4
+
5
+
6
+ async def execute_channel(method, sender: ValarChannelSender):
4
7
  thread = asyncio.to_thread(__execute__, method, sender)
5
8
  asyncio.create_task(thread)
6
9
 
7
10
 
8
- def __execute__(method, sender):
9
- sender.to_clients(None, [sender.client], 'start')
11
+ def __execute__(method, sender: ValarChannelSender):
12
+ sender.start()
10
13
  method(sender)
11
- sender.to_clients(None, [sender.client], 'stop')
12
-
13
-
14
+ sender.stop()
valar/channels/sender.py CHANGED
@@ -5,60 +5,112 @@ from datetime import datetime
5
5
  from asgiref.sync import async_to_sync
6
6
  from channels.layers import get_channel_layer
7
7
  from django.http import HttpRequest
8
-
8
+ import threading
9
9
  from .consumer import VALAR_CHANNEL_GROUP
10
- from ..classes.counter import Counter
11
10
 
12
11
 
13
- class ValarSocketSender:
14
- def __init__(self, request: HttpRequest, data=None):
12
+ class Channel:
13
+
14
+ def __init__(self, request: HttpRequest):
15
15
  body = json.loads(request.body)
16
- auth = request.headers.get('AUTH')
16
+ self.handler = body.get('handler')
17
+ self.url = body.get('url')
18
+ self.auth = body.get('auth')
19
+ self.broadcast = body.get('broadcast')
20
+ self.data = body.get('data')
21
+
22
+ def to_dict(self, status, payload):
23
+ data = {
24
+ 'handler': self.handler,
25
+ 'url': self.url,
26
+ 'auth': self.auth,
27
+ 'broadcast': self.broadcast,
28
+ 'timestamp': datetime.now().timestamp()
29
+ }
30
+ if status:
31
+ data.update({'status': status})
32
+ if payload:
33
+ data.update({'payload': payload})
34
+ return data
35
+
36
+
37
+ class Sender:
38
+
39
+ def __init__(self, request: HttpRequest):
17
40
  self.client = request.headers.get('CLIENT')
18
41
  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:
42
+ self.group_send = async_to_sync(get_channel_layer().group_send)
43
+
44
+
45
+ class ValarChannelSender(Sender):
46
+
47
+ def __init__(self, request: HttpRequest, interval=1):
48
+ super().__init__(request)
49
+ self.__channel__ = Channel(request)
50
+ self.data = self.__channel__.data
51
+ self.__payload__ = None
52
+ self.__loading__ = False
53
+ self.__thread__ = None
54
+ self.__lock__ = threading.Lock()
55
+ self.__interval__ = interval
56
+ if self.__channel__.auth and not self.uid:
25
57
  raise Exception('Unauthorized!')
26
58
 
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 [],
59
+ def _run(self):
60
+ while self.__loading__:
61
+ self.__emit__()
62
+ time.sleep(self.__interval__)
63
+
64
+ def start(self):
65
+ if self.__loading__:
66
+ return # 避免重复启动
67
+ self.__payload__ = None
68
+ self.__loading__ = True
69
+ self.__emit__('start')
70
+ self.__thread__ = threading.Thread(target=self._run, daemon=True)
71
+ self.__thread__.start()
72
+
73
+ def stop(self):
74
+ self.__payload__ = None
75
+ self.__loading__ = False
76
+ self.__emit__('stop')
77
+ if self.__thread__:
78
+ self.__thread__.join()
79
+ self.__thread__ = None
80
+
81
+ def load(self, payload):
82
+ with self.__lock__:
83
+ self.__payload__ = payload
84
+
85
+ def done(self, payload):
86
+ self.__emit__('done', payload)
87
+
88
+ def __emit__(self, status='proceed', data=None):
89
+ scope = 'broadcast' if self.__channel__.broadcast else 'client'
90
+ body = {
91
+ 'type': f'{scope}.emit',
92
+ 'data': self.__channel__.to_dict(status, data or self.__payload__),
93
+ 'clients': [self.client],
94
+ 'users': []
43
95
  }
96
+ self.group_send(VALAR_CHANNEL_GROUP, body)
44
97
 
45
98
 
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)
99
+ class ValarSocketSender(Sender):
100
+ def __init__(self, request: HttpRequest):
101
+ super().__init__(request)
102
+ if self.uid:
103
+ body = {'type': 'broadcast.emit', 'uid': self.uid, 'client': self.client}
104
+ self.group_send(VALAR_CHANNEL_GROUP, body)
49
105
 
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
106
+ def to_users(self, payload, users: list):
107
+ body = {'type': 'user.emit', 'data': payload, 'users': users}
108
+ self.group_send(VALAR_CHANNEL_GROUP, body)
57
109
 
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)
110
+ def to_clients(self, payload, clients: list):
111
+ body = {'type': 'client.emit', 'data': payload, 'clients': clients}
112
+ self.group_send(VALAR_CHANNEL_GROUP, body)
61
113
 
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)
114
+ def broadcast(self, payload):
115
+ body = {'type': 'broadcast.emit', 'data': payload}
116
+ self.group_send(VALAR_CHANNEL_GROUP, body)
valar/channels/views.py CHANGED
@@ -1,12 +1,11 @@
1
-
2
1
  from .executer import execute_channel
3
2
  from .mapping import ChannelMapping
4
- from .sender import ValarSocketSender
3
+ from .sender import ValarChannelSender
5
4
  from ..classes.valar_response import ValarResponse
6
5
 
7
6
 
8
7
  async def handel_channel(request, handler):
9
- sender = ValarSocketSender(request)
8
+ sender = ValarChannelSender(request)
10
9
  method = ChannelMapping().get_handler(handler)
11
10
  await execute_channel(method, sender)
12
11
  return ValarResponse(True)
@@ -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
+
@@ -3,13 +3,13 @@ import importlib
3
3
  from django.conf import settings
4
4
  from django.urls import path, include
5
5
 
6
+
6
7
  class AutoUrlPatternsMixin:
7
8
  name = None # 子类必须提供
9
+
8
10
  def set_url(self):
9
11
  root = settings.ROOT_URLCONF
10
12
  module = importlib.import_module(root)
11
13
  urlpatterns: list = getattr(module, 'urlpatterns')
12
14
  url = f'{self.name}.urls'
13
- urlpatterns.append(path('data/', include(url)))
14
-
15
-
15
+ urlpatterns.append(path('valar/', include(url)))
@@ -0,0 +1,80 @@
1
+ import json
2
+ from io import BytesIO
3
+ from django.conf import settings
4
+ from urllib3 import BaseHTTPResponse
5
+
6
+ from ..classes.singleton_meta import SingletonMeta
7
+
8
+
9
+ class ValarMinio(metaclass=SingletonMeta):
10
+
11
+ def __init__(self, client, entity):
12
+ self.client = client
13
+ self.bucket_name = f'{settings.BASE_DIR.name}.{entity}'.replace('_', '-').lower()
14
+ if client and not self.client.bucket_exists(self.bucket_name):
15
+ self.client.make_bucket(self.bucket_name)
16
+ self.client.set_bucket_policy(self.bucket_name, self.__generate_policy__())
17
+
18
+ @staticmethod
19
+ def get_object_name(_id, prop, file_name):
20
+ return f"{_id}-{prop}-{file_name}"
21
+
22
+ def upload(self, object_name, _bytes):
23
+ file_data = BytesIO(_bytes)
24
+ file_size = len(_bytes) # file.siz
25
+ self.client.put_object(
26
+ bucket_name=self.bucket_name,
27
+ object_name=object_name,
28
+ data=file_data,
29
+ length=file_size
30
+ )
31
+ return f'{self.bucket_name}/{object_name}'
32
+
33
+ def remove(self, path):
34
+ if path:
35
+ bucket_name, object_name = path.split('/')
36
+ self.client.remove_object(
37
+ bucket_name=bucket_name,
38
+ object_name=object_name
39
+ )
40
+
41
+ def read(self, object_name) -> BytesIO:
42
+ ret: BaseHTTPResponse = self.client.get_object(
43
+ bucket_name=self.bucket_name,
44
+ object_name=object_name
45
+ )
46
+ return BytesIO(ret.read())
47
+
48
+ def __generate_policy__(self):
49
+ return json.dumps({
50
+ "Version": "2012-10-17",
51
+ "Statement": [
52
+ {
53
+ "Sid": "",
54
+ "Effect": "Allow",
55
+ "Principal": {"AWS": "*"},
56
+ "Action": "s3:GetBucketLocation",
57
+ "Resource": f"arn:aws:s3:::{self.bucket_name}"
58
+ },
59
+ {
60
+ "Sid": "",
61
+ "Effect": "Allow",
62
+ "Principal": {"AWS": "*"},
63
+ "Action": "s3:ListBucket",
64
+ "Resource": f"arn:aws:s3:::{self.bucket_name}"
65
+ },
66
+ {
67
+ "Sid": "",
68
+ "Effect": "Allow",
69
+ "Principal": {"AWS": "*"},
70
+ "Action": "s3:GetObject",
71
+ "Resource": f"arn:aws:s3:::{self.bucket_name}/*"
72
+ },
73
+ {
74
+ "Sid": "",
75
+ "Effect": "Allow",
76
+ "Principal": {"AWS": "*"},
77
+ "Action": "s3:PutObject",
78
+ "Resource": f"arn:aws:s3:::{self.bucket_name}/*"
79
+ }
80
+ ]})
@@ -1,7 +1,8 @@
1
1
  from django.http import JsonResponse
2
2
 
3
+
3
4
  class ValarResponse(JsonResponse):
4
- def __init__(self, data, message='', code='info'):
5
+ def __init__(self, data=True, message='', code='info'):
5
6
  self.message = message
6
7
  self.code = code
7
8
  super(ValarResponse, self).__init__(data, safe=False)
valar/dao/__init__.py ADDED
@@ -0,0 +1,45 @@
1
+ from ..dao.abstract import AbstractDao
2
+ from ..dao.mon_dao import MonDao
3
+ from ..dao.orm_dao import OrmDao
4
+
5
+
6
+ class Dao(AbstractDao):
7
+
8
+ def __init__(self, entity, db='orm'):
9
+ self.dao = OrmDao(entity) if db == 'orm' else MonDao(entity)
10
+ self.db = db
11
+ self.entity = entity
12
+ self.name = self.dao.name
13
+ self.is_tree = self.dao.is_tree
14
+ self.fields = self.dao.fields
15
+ self.manager = self.dao.manager
16
+
17
+ def save_many(self, array: list):
18
+ self.dao.save_many(array)
19
+
20
+ def save_one(self, item, with_id=False):
21
+ return self.dao.save_one(item, with_id)
22
+
23
+ def delete_one(self, _id):
24
+ return self.dao.delete_one(_id)
25
+
26
+ def find_one(self, _id):
27
+ return self.dao.find_one(_id)
28
+
29
+ def find(self, conditions=None, orders=None, size=0, page=1):
30
+ return self.dao.find(conditions, orders, size, page)
31
+
32
+ def values(self, conditions, props):
33
+ return self.dao.values(conditions, props)
34
+
35
+ def update(self, template, conditions):
36
+ return self.dao.update(template, conditions)
37
+
38
+ def delete(self, conditions=None) -> list:
39
+ return self.dao.delete(conditions)
40
+
41
+ def serialize(self, o, code=None):
42
+ return self.dao.serialize(o, code)
43
+
44
+ def tree(self, root, conditions=None):
45
+ return self.dao.tree(root, conditions)
valar/dao/abstract.py ADDED
@@ -0,0 +1,106 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from bson import ObjectId
4
+
5
+ from ..dao.engine import ValarEngine
6
+
7
+
8
+ class AbstractField(ABC):
9
+ db = None
10
+ entity = None
11
+ prop = None
12
+ label = None
13
+ domain = None
14
+ refer = None
15
+
16
+ @abstractmethod
17
+ def to_dict(self):
18
+ pass
19
+
20
+
21
+ class AbstractDao(ABC):
22
+ engine = ValarEngine()
23
+ db = None
24
+ entity = None
25
+ name = None
26
+ is_tree = False
27
+ fields = {}
28
+
29
+ def props(self, domain=None):
30
+ return [prop for prop, field in self.fields.items() if field.domain == domain or domain is None]
31
+
32
+ def full_props(self):
33
+ return {
34
+ prop: {
35
+ "prop": prop,
36
+ "domain": field.domain,
37
+ "label": field.label,
38
+ }
39
+ for prop, field in self.fields.items()
40
+ }
41
+
42
+ def get_meta_field(self, prop) -> AbstractField:
43
+ return self.fields[prop]
44
+
45
+ @abstractmethod
46
+ def save_one(self, item, with_id=False):
47
+ pass
48
+
49
+ @abstractmethod
50
+ def save_many(self, array: list):
51
+ pass
52
+
53
+ @abstractmethod
54
+ def values(self, conditions, props):
55
+ pass
56
+
57
+ @abstractmethod
58
+ def delete_one(self, _id):
59
+ pass
60
+
61
+ @abstractmethod
62
+ def find_one(self, _id):
63
+ pass
64
+
65
+ @abstractmethod
66
+ def find(self, conditions=None, orders=None, size=0, page=1):
67
+ pass
68
+
69
+ @abstractmethod
70
+ def update(self, template, conditions):
71
+ pass
72
+
73
+ @abstractmethod
74
+ def delete(self, conditions=None) -> list:
75
+ pass
76
+
77
+ @abstractmethod
78
+ def serialize(self, o, code=None):
79
+ pass
80
+
81
+ @abstractmethod
82
+ def tree(self, root, conditions=None):
83
+ pass
84
+
85
+ def search(self, includes=None, excludes=None, orders=None):
86
+ conditions = [{"includes": includes or {}, "excludes": excludes or {}}]
87
+ results, _ = self.find(conditions, orders)
88
+ return results
89
+
90
+ def object_id(self, _id):
91
+ try:
92
+ return int(_id) if self.db == 'orm' else ObjectId(_id)
93
+ except Exception:
94
+ return None
95
+
96
+ # @abstractmethod
97
+ # def values(self, props, conditions, orders=None):
98
+ # pass
99
+ #
100
+ # @abstractmethod
101
+ # def group(self, props, conditions, orders=None):
102
+ # pass
103
+ #
104
+ # @abstractmethod
105
+ # def count(self, props, conditions):
106
+ # pass
File without changes
@@ -0,0 +1,48 @@
1
+ meta_field_key_defaults = {
2
+ 'valar.Meta': {
3
+ 'default': ('pick', ['db', 'entity', 'name', 'tree']),
4
+ },
5
+ 'valar.MetaView': {
6
+ 'list': ('pick', ['code', 'name']),
7
+ 'core': ('pick', ['name', 'enable', 'lock', 'property']),
8
+ 'style': ('pick', ['form_width', 'form_height', 'table_width', 'table_height']),
9
+ 'rest': ('pick',
10
+ ['allow_search', 'allow_order', 'allow_insert', 'allow_remove', 'allow_download', 'allow_upload']),
11
+ 'edit': ('pick', ['allow_edit', 'allow_edit_on_form', 'allow_edit_on_cell', 'allow_edit_on_sort']),
12
+ },
13
+ 'valar.MetaField': {
14
+ 'tool': (
15
+ 'pick',
16
+ [
17
+ 'name', 'domain', 'tool', 'refer', 'format'
18
+ ]
19
+ ),
20
+ 'rest': (
21
+ 'pick',
22
+ [
23
+ 'name', 'not_null',
24
+ 'allow_edit', 'allow_sort', 'allow_search', 'allow_download', 'allow_upload', 'allow_update'
25
+ ]
26
+ ),
27
+ 'table': (
28
+ 'pick',
29
+ [
30
+ 'name', 'unit', 'column_width', 'fixed', 'align', 'edit_on_table', 'hide_on_table',
31
+ 'header_color', 'cell_color'
32
+ ]
33
+ ),
34
+ 'form': (
35
+ 'pick',
36
+ [
37
+ 'name', 'span',
38
+ 'hide_on_form', 'hide_on_form_insert', 'hide_on_form_edit', 'hide_on_form_branch', 'hide_on_form_leaf'
39
+ ]
40
+ ),
41
+ },
42
+ 'vtest.Vala': {
43
+ 'simple': ('pick', ['id', 'name', 'text_field', 'boolean_field', 'integer_field', 'float_field']),
44
+ 'date': ('pick', ['id', 'name', 'date_field', 'datetime_field', 'time_field']),
45
+ 'special': ('pick', ['id', 'name', 'text_field', 'json_field', 'file', 'm2m']),
46
+ 'ref': ('pick', ['id', 'name', 'm2o', 'm2m', 'o2o_id']),
47
+ },
48
+ }