Skip to content

Commit

Permalink
0.0.9
Browse files Browse the repository at this point in the history
  • Loading branch information
elimintz committed Apr 22, 2020
1 parent 787db85 commit 54dc875
Show file tree
Hide file tree
Showing 73 changed files with 29,261 additions and 128 deletions.
1 change: 0 additions & 1 deletion examples/justpy.env

This file was deleted.

2 changes: 1 addition & 1 deletion justpy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from .justpy import *
__version__ = '0.0.8'
__version__ = '0.0.9'
46 changes: 45 additions & 1 deletion justpy/htmlcomponents.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class WebPage:
highcharts_theme = None
# One of ['avocado', 'dark-blue', 'dark-green', 'dark-unica', 'gray',
#'grid-light', 'grid', 'high-contrast-dark', 'high-contrast-light', 'sand-signika', 'skies', 'sunset']
allowed_events =['click', 'visibilitychange']


def __init__(self, **kwargs):
Expand All @@ -63,6 +64,7 @@ def __init__(self, **kwargs):
self.body_style = ''
self.body_classes = ''
self.reload_interval = None
self.events = []
self.dark = False # Set to True for Quasar dark mode (use for other dark modes also)
self.data = {}
WebPage.instances[self.page_id] = self
Expand Down Expand Up @@ -200,6 +202,26 @@ def build_list(self):
object_list.append(d)
return object_list

def on(self, event_type, func):
if event_type in self.allowed_events:
setattr(self, 'on_' + event_type, MethodType(func, self))
if event_type not in self.events:
self.events.append(event_type)
else:
raise Exception(f'No event of type {event_type} supported')

async def run_event_function(self, event_type, event_data, create_namespace_flag=True):
event_function = getattr(self, 'on_' + event_type)
if create_namespace_flag:
function_data = Dict(event_data)
else:
function_data = event_data
if inspect.iscoroutinefunction(event_function):
event_result = await event_function(function_data)
else:
event_result = event_function(function_data)
return event_result


class JustpyBaseComponent(Tailwind):
next_id = 1
Expand All @@ -220,6 +242,24 @@ def __init__(self, **kwargs):
self.events = []
self.allowed_events = []

def initialize(self, **kwargs):
for k, v in kwargs.items():
self.__setattr__(k, v)
for e in self.allowed_events:
for prefix in ['', 'on', 'on_']:
if prefix + e in kwargs.keys():
fn = kwargs[prefix + e]
if isinstance(fn, str):
fn_string = f'def oneliner{self.id}(self, msg):\n {fn}'
exec(fn_string)
self.on(e, locals()[f'oneliner{self.id}'])
else:
self.on(e, fn)
break
for com in ['a', 'add_to']:
if com in kwargs.keys():
kwargs[com].add_component(self)

def delete(self):
if self.needs_deletion:
if self.delete_flag:
Expand Down Expand Up @@ -357,8 +397,10 @@ def __init__(self, **kwargs):
self.style = ''
self.directives = []
self.data = {}
self.drag_options = None
self.allowed_events = ['click', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave', 'input', 'change',
'after', 'before', 'keydown', 'keyup', 'keypress', 'focus', 'blur', 'submit']
'after', 'before', 'keydown', 'keyup', 'keypress', 'focus', 'blur', 'submit',
'dragstart', 'dragover', 'drop']
self.events = []
self.additional_properties = []
self.event_propagation = True # If True events are propagated
Expand Down Expand Up @@ -488,6 +530,8 @@ def convert_object_to_dict(self):
d['scoped_slots'][s] = self.scoped_slots[s].convert_object_to_dict()
if self.additional_properties:
d['additional_properties'] = self.additional_properties
if self.drag_options:
d['drag_options'] = self.drag_options
return d


Expand Down
98 changes: 68 additions & 30 deletions justpy/justpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
from .pandas import *
from .routing import Route, SetRoute
from .utilities import run_task, create_delayed_task
import uvicorn, logging, uuid, sys, os
import uvicorn, logging, uuid, sys, os, traceback, fnmatch
from ssl import PROTOCOL_SSLv23

current_module = sys.modules[__name__]
current_dir = os.path.dirname(current_module.__file__)
print(current_dir.replace('\\', '/'))
print(f'Module directory: {current_dir}, Application directory: {os.getcwd()}')

config = Config('justpy.env')
Expand Down Expand Up @@ -55,14 +56,30 @@
AGGRID = config('AGGRID', cast=bool, default=True)
AGGRID_ENTERPRISE = config('AGGRID_ENTERPRISE', cast=bool, default=False)

NO_INTERNET = config('NO_INTERNET', cast=bool, default=False)

def create_component_file_list():
file_list = []
component_dir = f'{STATIC_DIRECTORY}\\components'
if os.path.isdir(component_dir):
for file in os.listdir(component_dir):
if fnmatch.fnmatch(file, '*.js'):
file_list.append(f'/components/{file}')
return file_list


templates = Jinja2Templates(directory=TEMPLATES_DIRECTORY)

component_file_list = create_component_file_list()

template_options = {'tailwind': TAILWIND, 'quasar': QUASAR, 'highcharts': HIGHCHARTS, 'aggrid': AGGRID, 'aggrid_enterprise': AGGRID_ENTERPRISE,
'static_name': STATIC_NAME}
'static_name': STATIC_NAME, 'component_file_list': component_file_list, 'no_internet': NO_INTERNET}
logging.basicConfig(level=LOGGING_LEVEL, format='%(levelname)s %(module)s: %(message)s')

templates = Jinja2Templates(directory=TEMPLATES_DIRECTORY)

app = Starlette(debug=DEBUG)
app.mount(STATIC_ROUTE, StaticFiles(directory=STATIC_DIRECTORY), name=STATIC_NAME)
app.mount('/templates', StaticFiles(directory=current_dir + '/templates'), name='templates')
app.add_middleware(GZipMiddleware)
if SSL_KEYFILE and SSL_CERTFILE:
app.add_middleware(HTTPSRedirectMiddleware)
Expand Down Expand Up @@ -134,7 +151,7 @@ async def get(self, request):
page_options = {'reload_interval': load_page.reload_interval, 'body_style': load_page.body_style,
'body_classes': load_page.body_classes, 'css': load_page.css, 'head_html': load_page.head_html, 'body_html': load_page.body_html,
'display_url': load_page.display_url, 'dark': load_page.dark, 'title': load_page.title,
'highcharts_theme': load_page.highcharts_theme, 'debug': load_page.debug,
'highcharts_theme': load_page.highcharts_theme, 'debug': load_page.debug, 'events': load_page.events,
'favicon': load_page.favicon if load_page.favicon else FAVICON}
if load_page.use_cache:
page_dict = load_page.cache
Expand Down Expand Up @@ -167,7 +184,10 @@ async def post(self, request):
data_dict['event_data']['session_id'] = session_id

# data_dict['event_data']['session'] = request.session
result = await handle_event(data_dict, com_type=1)
msg_type = data_dict['type']
data_dict['event_data']['msg_type'] = msg_type
page_event = True if msg_type == 'page_event' else False
result = await handle_event(data_dict, com_type=1, page_event=page_event)
if result:
if LATENCY:
await asyncio.sleep(LATENCY / 1000)
Expand All @@ -193,7 +213,8 @@ async def on_connect(self, websocket):
logging.debug(f'Websocket {JustpyEvents.socket_id} connected')
JustpyEvents.socket_id += 1
#Send back socket_id to page
await websocket.send_json({'type': 'websocket_update', 'data': websocket.id})
# await websocket.send_json({'type': 'websocket_update', 'data': websocket.id})
WebPage.loop.create_task(websocket.send_json({'type': 'websocket_update', 'data': websocket.id}))

async def on_receive(self, websocket, data):
"""
Expand All @@ -202,17 +223,38 @@ async def on_receive(self, websocket, data):
logging.debug('%s %s',f'Socket {websocket.id} data received:', data)
data_dict = json.loads(data)
msg_type = data_dict['type']
# data_dict['event_data']['type'] = msg_type
if msg_type == 'connect':
# Initial message sent from browser after connection is established
await self._connect(websocket, data_dict)
# WebPage.sockets is a dictionary of dictionaries
# First dictionary key is page id
# Second dictionary key is socket id
page_key = data_dict['page_id']
websocket.page_id = page_key
if page_key in WebPage.sockets:
WebPage.sockets[page_key][websocket.id] = websocket
else:
WebPage.sockets[page_key] = {websocket.id: websocket}
return
if msg_type == 'event' or msg_type == 'page_event':
# Message sent when an event occurs in the browser
session_cookie = websocket.cookies.get(SESSION_COOKIE_NAME)
if SESSIONS and session_cookie:
session_id = cookie_signer.unsign(session_cookie).decode("utf-8")
data_dict['event_data']['session_id'] = session_id
# await self._event(data_dict)
data_dict['event_data']['msg_type'] = msg_type
page_event = True if msg_type == 'page_event' else False
WebPage.loop.create_task(handle_event(data_dict, com_type=0, page_event=page_event))
return
if msg_type == 'event':
if msg_type == 'zzz_page_event':
# Message sent when an event occurs in the browser
session_cookie = websocket.cookies.get(SESSION_COOKIE_NAME)
if SESSIONS and session_cookie:
session_id = cookie_signer.unsign(session_cookie).decode("utf-8")
data_dict['event_data']['session_id'] = session_id
await self._event(data_dict)
data_dict['event_data']['msg_type'] = msg_type
WebPage.loop.create_task(handle_event(data_dict, com_type=0, page_event=True))
return

async def on_disconnect(self, websocket, close_code):
Expand All @@ -222,6 +264,7 @@ async def on_disconnect(self, websocket, close_code):
if not WebPage.sockets[pid]:
WebPage.sockets.pop(pid)
await WebPage.instances[pid].on_disconnect(websocket) # Run the specific page disconnect function
# WebPage.loop.create_task(WebPage.instances[pid].on_disconnect(websocket))
if MEMORY_DEBUG:
print('************************')
print('Elements: ', len(JustpyBaseComponent.instances), JustpyBaseComponent.instances)
Expand All @@ -231,25 +274,9 @@ async def on_disconnect(self, websocket, close_code):
print(f'Memory used: {process.memory_info().rss:,}')
print('************************')

async def _connect(self, websocket, data_dict):
# WebPage.sockets is a dictionary of dictionaries
# First dictionary key is page id
# Second dictionary key is socket id
page_key = data_dict['page_id']
websocket.page_id = page_key
if page_key in WebPage.sockets:
WebPage.sockets[page_key][websocket.id] = websocket
else:
WebPage.sockets[page_key] = {websocket.id: websocket}

async def _event(self, data_dict):
# com_type 0: websocket, com_type 1: ajax
# await handle_event(data_dict, com_type=0)
WebPage.loop.create_task(handle_event(data_dict, com_type=0))
return


async def handle_event(data_dict, com_type=0):
async def handle_event(data_dict, com_type=0, page_event=False):
# com_type 0: websocket, con_type 1: ajax
connection_type = {0: 'websocket', 1: 'ajax'}
logging.info('%s %s %s', 'In event handler:', connection_type[com_type], str(data_dict))
Expand All @@ -266,7 +293,12 @@ async def handle_event(data_dict, com_type=0):
if event_data['event_type'] == 'page_update':
build_list = p.build_list()
return {'type': 'page_update', 'data': build_list}
c = JustpyBaseComponent.instances[event_data['id']]

if page_event:
c = p
else:
c = JustpyBaseComponent.instances[event_data['id']]

try:
before_result = await c.run_event_function('before', event_data, True)
except:
Expand All @@ -277,7 +309,9 @@ async def handle_event(data_dict, com_type=0):
except Exception as e:
# raise Exception(e)
event_result = None
logging.info('%s %s', 'Event result:', '\u001b[47;1m\033[93mAttempting to run event handler:' + str(e) + '\033[0m')
# logging.info('%s %s', 'Event result:', '\u001b[47;1m\033[93mAttempting to run event handler:' + str(e) + '\033[0m')
logging.info('%s %s', 'Event result:', '\u001b[47;1m\033[93mAttempting to run event handler:\033[0m')
logging.debug('%s', traceback.format_exc())

# If page is not to be updated, the event_function should return anything but None

Expand All @@ -293,7 +327,12 @@ async def handle_event(data_dict, com_type=0):
except:
pass
if com_type == 1 and event_result is None:
return {'type': 'page_update', 'data': build_list}
dict_to_send = {'type': 'page_update', 'data': build_list,
'page_options': {'display_url': p.display_url,
'title': p.title,
'redirect': p.redirect, 'open': p.open,
'favicon': p.favicon}}
return dict_to_send


def justpy(func=None, *, start_server=True, websockets=True, host=HOST, port=PORT, startup=None, **kwargs):
Expand Down Expand Up @@ -335,4 +374,3 @@ def convert_dict_to_object(d):
return obj



6 changes: 3 additions & 3 deletions justpy/quasarcomponents.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ def __init__(self, **kwargs):
@parse_dict
class QBtnGroup(QDiv):

slots= ['defualt_slot']
slots= ['default_slot']
html_tag = 'q-btn-group'

def __init__(self, **kwargs):
Expand Down Expand Up @@ -651,7 +651,7 @@ def __init__(self, **kwargs):
self.type = 'float'
self.prop_list = ['input', 'boundary-links', 'direction-links', 'boundary-numbers', 'ellipses', 'value', 'min',
'max', 'max-pages', 'disable', 'color', 'text-color', 'input-style', 'input-class', 'size',
'icon-prev', 'icon-next', 'icon-first', 'icon-last']
'icon-prev', 'icon-next', 'icon-first', 'icon-last', 'ripple']
self.allowed_events = ['input']


Expand Down Expand Up @@ -1442,7 +1442,7 @@ def __init__(self, **kwargs):
self.pagination = False
self.selected = []
super().__init__(**kwargs)
self.attributes = ['fullscreen', 'losding', 'columns', 'visible-columns', 'title', 'hide-header', 'hide-bottom',
self.attributes = ['fullscreen', 'loading', 'columns', 'visible-columns', 'title', 'hide-header', 'hide-bottom',
'separator', 'wrap-cells', 'no-data-label', 'no-results-label', 'loading-label',
'filter', 'filter-method', 'data', 'row-key', 'rows-per-page-label', 'pagination-label',
'pagination', 'rows-per-page-options', 'selected-rows-label', 'selection', 'selected',
Expand Down
Loading

0 comments on commit 54dc875

Please sign in to comment.