def _comp_init_app_component(self, property_values): # Pop special attribute property_values.pop('flx_is_app', None) # Pop and apply id if given custom_id = property_values.pop('flx_id', None) # Pop session or derive from active component self._session = None session = property_values.pop('flx_session', None) if session is not None: self._session = session else: active = loop.get_active_components() # Note that self is active too active = active[-2] if len(active) > 1 else None if active is not None: self._session = active._session else: if not this_is_js(): self._session = manager.get_default_session() # Register this component with the session (sets _id and _uid) if self._session is None: raise RuntimeError('%s needs a session!' % (custom_id or self._id)) self._session._register_component(self, custom_id) self._root = self._session.app # Return whether this instance was instantiated locally return custom_id is None
def _comp_init_property_values(self, property_values): # Init more local_inst = self._comp_init_app_component( property_values) # pops items # Call original method, only set props if this is instantiated "by the local" props2set = {} if local_inst else property_values super()._comp_init_property_values(props2set) if this_is_js(): # This is a proxy PyComponent in JavaScript assert len(property_values.keys()) == 0 else: # This is a proxy JsComponent in Python # Instantiate JavaScript version of this class if local_inst is True: # i.e. only if Python "instantiated" it property_values['flx_has_proxy'] = True active_components = [ c for c in loop.get_active_components()[:-1] if isinstance(c, (PyComponent, JsComponent)) ] self._session.send_command('INSTANTIATE', self.__jsmodule__, self.__class__.__name__, self._id, self._flx_init_args, property_values, active_components) del self._flx_init_args
def time2str(t, utc_offset=None): """Convert a time int into a textual representation. If utc_offset is None, the representation is in local time. Otherwise the given offset (in hours) is used. Use utc_offset=0 for UTC. In all cases the zone is explicit in the result. """ t = to_time_int(t) if this_is_js(): # pragma: no cover if utc_offset is None: utc_offset = -(Date(t * 1000).getTimezoneOffset() // 60) t += utc_offset * 3600 s = Date(t * 1000).toISOString() s = s.split(".")[0] if utc_offset == 0: s += "Z" else: s += f"{utc_offset:+03.0f}" else: # py import datetime if utc_offset is None: utc_offset = ( datetime.datetime.fromtimestamp(t) - datetime.datetime.utcfromtimestamp(t)).total_seconds() // 3600 tz = datetime.timezone(datetime.timedelta(hours=utc_offset)) dt = datetime.datetime.fromtimestamp(t, tz) if utc_offset == 0: s = dt.strftime("%Y-%m-%dT%H:%M:%SZ") else: s = dt.strftime("%Y-%m-%dT%H:%M:%S%z") return s
def _comp_init_property_values(self, property_values): # Init more local_inst = self._comp_init_app_component(property_values) # pops items # Call original method, only set props if this is instantiated "by the local", # except implicit setters (properties who's values are callables). props2set = {} if local_inst else property_values for name in list(property_values.keys()): if callable(property_values[name]): props2set[name] = property_values.pop(name) super()._comp_init_property_values(props2set) if this_is_js(): # This is a proxy PyComponent in JavaScript assert len(property_values.keys()) == 0 else: # This is a proxy JsComponent in Python # Instantiate JavaScript version of this class if local_inst is True: # i.e. only if Python "instantiated" it property_values['flx_has_proxy'] = True active_components = [c for c in loop.get_active_components()[:-1] if isinstance(c, (PyComponent, JsComponent))] self._session.send_command('INSTANTIATE', self.__jsmodule__, self.__class__.__name__, self._id, self._flx_init_args, property_values, active_components) del self._flx_init_args
def _comp_init_app_component(self, property_values): # Pop special attribute property_values.pop('flx_is_app', None) # Pop and apply id if given custom_id = property_values.pop('flx_id', None) # Pop session or derive from active component self._session = None session = property_values.pop('flx_session', None) if session is not None: self._session = session else: active = loop.get_active_components() # Note that self is active too active = active[-2] if len(active) > 1 else None if active is not None: self._session = active._session else: if not this_is_js(): self._session = manager.get_default_session() # Register this component with the session (sets _id and _uid) if self._session is None: raise RuntimeError('%s needs a session!' % (custom_id or self._id)) self._session._register_component(self, custom_id) self._root = self._session.app # Return whether this instance was instantiated locally return custom_id is None
def _comp_init_property_values(self, property_values): # This is a good time to register with the session, and # instantiate the proxy class. Property values have been set at this # point, but init() has not yet been called. # Property values must be poped when consumed so that the remainer is used for # instantiation of the Widget # Keep track of what events are registered at the proxy self.__event_types_at_proxy = [] # Init more self._comp_init_app_component(property_values) # pops items # Pop whether this local instance has a proxy at the other side self._has_proxy = property_values.pop('flx_has_proxy', False) # Call original method super()._comp_init_property_values(property_values) if this_is_js(): # This is a local JsComponent in JavaScript self._event_listeners = [] else: # This is a local PyComponent in Python # A PyComponent always has a corresponding proxy in JS self._ensure_proxy_instance(False)
def now(): """Get the current time in seconds, as a float.""" if this_is_js(): return Date().getTime() / 1000 else: import time return time.time()
def _proxy_emitter(self, name, *args, **kwargs): """ To handle use of placeholder emitters. """ # todo: I am not sure yet whether to allow or disallow it. We disallow now; # we can always INVOKE the emitter at the other side if that proves needed if this_is_js(): logger.error('Cannot use emitters of a PyComponent in JS.') else: logger.error('Cannot use emitters of a JsComponent in Py.')
def _proxy_emitter(self, name, *args, **kwargs): """ To handle use of placeholder emitters. """ # todo: I am not sure yet whether to allow or disallow it. We disallow now; # we can always INVOKE the emitter at the other side if that proves needed if this_is_js(): logger.error('Cannot use emitters of a PyComponent in JS.') else: logger.error('Cannot use emitters of a JsComponent in Py.')
def get_year_month_day(t): if this_is_js(): d = Date(t * 1000) return d.getFullYear(), d.getMonth() + 1, d.getDate() else: import datetime dt = datetime.datetime.fromtimestamp(t) return dt.year, dt.month, dt.day
def get_tags_and_parts_from_string(s=""): """Given a string, return a sorted list of tags, and a list of text parts that can be concatenated into the (nearly) original string. A bit of normalization is done as well: * the tags are made lowercase. * Leading and standalone # symbols are cleared. * Add a space between tags that are #glued#together. * Trim trailing whitespace. A valid tag starts with '#' followed by any alphanumerical characters, including dash, underscore, forward slash, and anything above 127. """ parts = [] tags = {} tag_start = -1 tag_end = 0 for i in range(len(s) + 1): cc = ord(s[i]) if i < len(s) else 35 if tag_start < 0: if cc == 35: # hash symbol (#) tag_start = i text = s[tag_end:i] if len(text) > 0: if len(parts) > 0 and parts[-1][0] != "#": parts[-1] = parts[-1] + text else: parts.append(text) else: if not is_valid_tag_charcode(cc): text = s[tag_start:i] if len(text) > 1: # dont count the # symbol tag = text.lower() parts.append(tag) tags[tag] = tag if cc == 35: parts.append(" ") # add a space #between#tags tag_start = i else: tag_start = -1 tag_end = i if len(parts) > 0: last = parts[-1].rstrip() if len(last) > 0: parts[-1] = last else: parts.pop(-1) tags = tags.values() if not this_is_js(): tags = list(tags) tags.sort() return tags, parts
def dispose(self): if this_is_js(): # The server is leading ... raise RuntimeError('Cannot dispose a PyComponent from JS.') else: # Disposing a JsComponent from JS is like invoking an action; # we don't actually dispose ourselves just yet. if self._session.status > 0: self._session.send_command('INVOKE', self._id, 'dispose', []) else: super().dispose()
def dispose(self): if this_is_js(): # The server is leading ... raise RuntimeError('Cannot dispose a PyComponent from JS.') else: # Disposing a JsComponent from JS is like invoking an action; # we don't actually dispose ourselves just yet. if self._session.status > 0: self._session.send_command('INVOKE', self._id, 'dispose', []) else: super().dispose()
def to_time_int(t): """Get a time (in int seconds since epoch), given float/str input. String inputs can be: * 'now': get current time. * E.g. '2018-04-24 11:23:00' for a local time (except in Safari :/). * E.g. '2018-04-24 11:23:00Z' for a time in UTC. * E.g. '2018-04-24 11:23:00+0200' for AMS summertime in this case. In the above, one can use a 'T' instead of a space between data and time to comply with ISO 8601. """ if this_is_js(): if isinstance(t, Date): t = t.getTime() / 1000 if isinstance(t, str): t = t.strip() if this_is_js(): # pragma: no cover if t.lower() == "now": t = Date().getTime() / 1000 else: if t.count(" ") == 1 and t[10] == " ": t = t.replace(" ", "T") # Otherwise Safari wont take it t = Date(t).getTime() / 1000 # Let browser handle date parsing else: # py import datetime t = t.replace("T", " ") if t.lower() == "now": t = datetime.datetime.now().timestamp() elif t.endswith("Z") or t[-5] in "-+": # ISO 8601 t = (t[:-1] + "+0000") if t.endswith("Z") else t t = datetime.datetime.strptime( t, "%Y-%m-%d %H:%M:%S%z").timestamp() else: t = datetime.datetime.strptime( t, "%Y-%m-%d %H:%M:%S").timestamp() if not isinstance(t, (int, float)): raise RuntimeError(f"Time must be a number, not {t!r}") return int(t)
def __init__(self, *init_args, **kwargs): # Need to overload this to handle init_args if this_is_js(): # This is a proxy PyComponent in JavaScript. # Always instantiated via an INSTANTIATE command from Python. assert len(init_args) == 0 if 'flx_id' not in kwargs: raise RuntimeError('Cannot instantiate a PyComponent from JS.') super().__init__(**kwargs) else: # This is a proxy JsComponent in Python. # Can be instantiated in Python, self._flx_init_args = init_args super().__init__(**kwargs)
def _emit_at_proxy(self, ev): """ Action used by the local component to push an event to the proxy component. If the event represents a property-update, the mutation is applied, otherwise the event is emitted here. """ if not this_is_js(): ev = Dict(ev) if ev.type in self.__properties__ and hasattr(ev, 'mutation'): # Mutate the property - this will cause an emit if ev.mutation == 'set': super()._mutate(ev.type, ev.new_value) else: super()._mutate(ev.type, ev.objects, ev.mutation, ev.index) else: self.emit(ev.type, ev)
def __init__(self, *init_args, **kwargs): # Need to overload this to handle init_args if this_is_js(): # This is a proxy PyComponent in JavaScript. # Always instantiated via an INSTANTIATE command from Python. assert len(init_args) == 0 if 'flx_id' not in kwargs: raise RuntimeError('Cannot instantiate a PyComponent from JS.') super().__init__(**kwargs) else: # This is a proxy JsComponent in Python. # Can be instantiated in Python, self._flx_init_args = init_args super().__init__(**kwargs)
def _emit_at_proxy(self, ev): """ Action used by the local component to push an event to the proxy component. If the event represents a property-update, the mutation is applied, otherwise the event is emitted here. """ if not this_is_js(): ev = Dict(ev) if ev.type in self.__properties__ and hasattr(ev, 'mutation'): # Mutate the property - this will cause an emit if ev.mutation == 'set': super()._mutate(ev.type, ev.new_value) else: super()._mutate(ev.type, ev.objects, ev.mutation, ev.index) else: self.emit(ev.type, ev)
def generate_uid(): """Generate a unique id in the form of an 8-char string. The value is used to uniquely identify the record of one user. Assuming a user who has been creating 100 records a day, for 20 years (about 1M records), the chance of a collision for a new record is about 1 in 50 milion. """ chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" n = 8 nchars = len(chars) # 52, so 52**8 => 53459728531456 possibilities if this_is_js(): ar = window.Uint32Array(n) window.crypto.getRandomValues(ar) return "".join([chars[ar[i] % nchars] for i in range(n)]) else: return "".join([chars[int(random() * nchars)] for i in range(n)])
def _comp_init_property_values(self, property_values): # This is a good time to register with the session, and # instantiate the proxy class. Property values have been set at this # point, but init() has not yet been called. # Keep track of what events are registered at the proxy self.__event_types_at_proxy = [] # Init more self._comp_init_app_component(property_values) # pops items # Pop whether this local instance has a proxy at the other side self._has_proxy = property_values.pop('flx_has_proxy', False) # Call original method super()._comp_init_property_values(property_values) if this_is_js(): # This is a local JsComponent in JavaScript self._event_listeners = [] else: # This is a local PyComponent in Python # A PyComponent always has a corresponding proxy in JS self._ensure_proxy_instance(False)
|________| |________| * Assume/assert that A1 < A2 and B1 < B2 * No ovelap : A2 <= B1 or A1 >= B2 * Some overlap: A2 > B1 and A1 < B2 * A fully in B: A1 >= B1 and A2 <= B2 * B fully in A: A1 <= B1 and A2 >= B2 """ from pscript import this_is_js from pscript.stubs import Math, Date, JSON, window, console, RawJS if this_is_js(): # pragma: no cover tools = window.tools utils = window.utils dt = window.dt # noqa random = Math.random def to_int(x): RawJS(""" x = Number(x) if (!isFinite(x)) { var e = new Error("TypeError: Cannot convert to int"); e.name = "TypeError"; throw e; } """) return Math.floor(x)
def _create_one_year_of_data(self, y): PSCRIPT_OVERLOAD = False # noqa now = dt.now() nowyear, nowmonth, nowday = dt.get_year_month_day(dt.now()) sy = str(y) rr = [] rpd = list(range(3, 9)) # number of records per day for m in range(1, 13): sm = f"{m:02}" # For each month, we select tags from the tag groups, # we may not have all, and we may have duplicates. tags = [] for i in range(1): tag_group = self._tag_groups1[int(random() * len(self._tag_groups1))] for tag in tag_group: tags.append(tag) for i in range(2): tag_group = self._tag_groups2[int(random() * len(self._tag_groups2))] for tag in tag_group: tags.append(tag) for d in range(1, 32): sd = f"{d:02}" # Don't make records in the current future if y > nowyear: continue elif y == nowyear and m > nowmonth: continue elif y == nowyear and m == nowmonth and d > nowday: continue # Is this date ok? if this_is_js(): # pragma: no cover weekday = Date(f"{sy}-{sm}-{sd}").getDay() # 0 is Sunday # Put some predictable stuff on today, whatever day it is. if y == nowyear and m == nowmonth and d == nowday: for start, stop, tag in [ ("08:51", "09:11", "#admin"), ("09:11", "10:27", "#client1 #meeting"), ("10:29", "11:52", "#client1 #code"), ("12:51", "13:32", "#client1 #code"), ("13:32", "14:28", "#client2 #meeting"), ("14:34", "16:11", "#client2 #design"), ]: t1 = dt.to_time_int(f"{sy}-{sm}-{sd}T{start}:00") t2 = dt.to_time_int(f"{sy}-{sm}-{sd}T{stop}:00") if t2 > now: continue works = [ "work", "stuff", "things", "administration" ] ds = "Did some " + works[int( random() * len(works))] ds += " " + tag record = self.records.create(t1, t2, ds) record.st = now rr.append(record) continue elif weekday not in (1, 2, 3, 4, 5): continue # no NaN (invalid date) or weekends else: if d > 28: continue # on Python, during tests t1 = dt.to_time_int(f"{sy}-{sm}-{sd}T08:00:00") for h in range(rpd[int(random() * len(rpd))]): tag = tags[int(random() * len(tags))] t1 += [0, 10 * 60, 20 * 60][int(random() * 3)] # pause in secs t2 = t1 + 60 * (60 + int(random() * 120)) # 1-3 hours if t2 > now - 60: break works = ["work", "stuff", "things", "administration"] ds = "Did some " + works[int(random() * len(works))] ds += " " + tag record = self.records.create(t1, t2, ds) record.st = now t1 = t2 # next rr.append(record) # Store records self.records._put_received(*rr)
def keep_checking_size_of(self, ob, check=True): """ This is a service that the session provides. """ # Gets called by the Widget class for toplevel widgets. That # is, toplevel to Flexx: they might not be toplevel for the # browser. This method will make sure that they know their size # in any case, at least once each second. if check: self.instances_to_check_size[ob.id] = ob else: self.instances_to_check_size.pop(ob.id, None) def _check_size_of_objects(self): for ob in self.instances_to_check_size.values(): if ob._disposed is False: ob.check_real_size() # In Python, we need some extras for the serializer to work if this_is_js(): # Include bsdf.js window.flexx = Flexx() bsdf = RawJS("flexx.require('bsdf')") serializer = bsdf.BsdfSerializer() window.flexx.serializer = serializer else: # Import vendored bsdf lite module from . import bsdf_lite as bsdf serializer = bsdf.BsdfLiteSerializer() serializer.__module__ = __name__
def keep_checking_size_of(self, ob, check=True): """ This is a service that the session provides. """ # Gets called by the Widget class for toplevel widgets. That # is, toplevel to Flexx: they might not be toplevel for the # browser. This method will make sure that they know their size # in any case, at least once each second. if check: self.instances_to_check_size[ob.id] = ob else: self.instances_to_check_size.pop(ob.id, None) def _check_size_of_objects(self): for ob in self.instances_to_check_size.values(): if ob._disposed is False: ob.check_real_size() # In Python, we need some extras for the serializer to work if this_is_js(): # Include bsdf.js window.flexx = Flexx() bsdf = RawJS("flexx.require('bsdf')") serializer = bsdf.BsdfSerializer() window.flexx.serializer = serializer else: # Import vendored bsdf lite module from . import bsdf_lite as bsdf serializer = bsdf.BsdfLiteSerializer() serializer.__module__ = __name__