Compare commits
4 Commits
58035c4ee7
...
6ebeff0f32
Author | SHA1 | Date |
---|---|---|
phryk | 6ebeff0f32 | |
phryk | eca0865fa0 | |
phryk | a4b88e1208 | |
phryk | 013d906c66 |
32
example.py
32
example.py
|
@ -6,6 +6,8 @@ import json
|
|||
import requests
|
||||
import datetime
|
||||
import flask
|
||||
import numpy
|
||||
import pandas
|
||||
import poobrains
|
||||
|
||||
from flask import redirect
|
||||
|
@ -183,13 +185,15 @@ class StockWeekly(poobrains.svg.plot.Dataset):
|
|||
class ConstrainedRandom(poobrains.svg.plot.Dataset):
|
||||
|
||||
def fill(self, magnitude=2, length=16):
|
||||
|
||||
#app.debugger.set_trace()
|
||||
magnitude, length = int(magnitude), int(length)
|
||||
|
||||
ranges = []
|
||||
for i in range(0, magnitude):
|
||||
ranges.append(sorted((random.random(), random.random())))
|
||||
|
||||
#rawdata = pandas.Series(name=self.__class__.__name__, index=[0])
|
||||
rawdata = []
|
||||
for i in range(0, length):
|
||||
|
||||
y = random.random()
|
||||
|
@ -204,7 +208,31 @@ class ConstrainedRandom(poobrains.svg.plot.Dataset):
|
|||
|
||||
y *= math.sin(2 * math.pi * float(i) / length)
|
||||
|
||||
self.add_datapoint(i, y) #, error_lower=y - r[0], error_upper=r[1] - y)
|
||||
#self.add_datapoint(i, y) #, error_lower=y - r[0], error_upper=r[1] - y)
|
||||
#rawdata.iat[i] = y
|
||||
rawdata.append(y)
|
||||
|
||||
#self.data.iat[0] = rawdata
|
||||
self.data = pandas.DataFrame(data=rawdata)
|
||||
|
||||
|
||||
class Sine(poobrains.svg.plot.Dataset):
|
||||
|
||||
def fill(self, length=10):
|
||||
|
||||
import pudb; pudb.set_trace()
|
||||
inc = (2 * math.pi) / (length - 1)
|
||||
#inc = 1.0 / (length - 1)
|
||||
|
||||
rawdata = {}
|
||||
x = 0
|
||||
|
||||
for _ in range(0, length):
|
||||
|
||||
rawdata[x] = math.sin(x)
|
||||
x += inc
|
||||
|
||||
self.data = pandas.DataFrame(columns=['sine'], index=rawdata.keys(), data=rawdata.values())
|
||||
|
||||
|
||||
class RandomMap(poobrains.svg.geo.GeoData):
|
||||
|
|
|
@ -131,6 +131,12 @@ class DummySession(werkzeug.datastructures.CallbackDict, flask.sessions.SessionM
|
|||
modified = False
|
||||
accessed = False
|
||||
|
||||
new_session_funcs = set()
|
||||
def new_session(f):
|
||||
|
||||
new_session_funcs.add(f)
|
||||
return f
|
||||
|
||||
|
||||
class Session(werkzeug.datastructures.CallbackDict, flask.sessions.SessionMixin):
|
||||
|
||||
|
@ -145,6 +151,10 @@ class Session(werkzeug.datastructures.CallbackDict, flask.sessions.SessionMixin)
|
|||
self.key = nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE)
|
||||
self.crypto = nacl.secret.SecretBox(self.key)
|
||||
|
||||
# call all @new_session decorated functions with this session object
|
||||
for func in new_session_funcs:
|
||||
func(self)
|
||||
|
||||
else:
|
||||
|
||||
if not key:
|
||||
|
@ -156,15 +166,21 @@ class Session(werkzeug.datastructures.CallbackDict, flask.sessions.SessionMixin)
|
|||
self.key = key
|
||||
self.crypto = nacl.secret.SecretBox(self.key)
|
||||
|
||||
plaintext = self.crypto.decrypt(self.sessiondata.data)
|
||||
plaintext = self.crypto.decrypt(bytes(self.sessiondata.data))
|
||||
for k, v in pickle.loads(plaintext).items():
|
||||
self[k] = v
|
||||
|
||||
self.sid = sid
|
||||
|
||||
except storage.SessionData.DoesNotExist:
|
||||
except storage.SessionData.DoesNotExist: # throw away unknown sids and create new sessiondata
|
||||
self.sessiondata = storage.SessionData()
|
||||
self.sid = None
|
||||
self.key = nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE)
|
||||
self.crypto = nacl.secret.SecretBox(self.key)
|
||||
|
||||
# call all @new_session decorated functions with this session object
|
||||
for func in new_session_funcs:
|
||||
func(self)
|
||||
|
||||
def on_update(self):
|
||||
self.modified = True
|
||||
|
|
|
@ -35,7 +35,6 @@ class BytesParamType(poobrains.form.types.ParamType):
|
|||
if type(e) is ValueError:
|
||||
raise
|
||||
|
||||
app.debugger.set_trace()
|
||||
|
||||
BYTES = BytesParamType()
|
||||
|
||||
|
|
|
@ -11,12 +11,9 @@ from . import geo
|
|||
SVG = base.SVG
|
||||
|
||||
Dataset = plot.Dataset
|
||||
Datapoint = plot.Datapoint
|
||||
|
||||
StoredDataset = plot.StoredDataset
|
||||
StoredDatapoint = plot.StoredDatapoint
|
||||
|
||||
#GeoData = geo.GeoData
|
||||
GeoData = geo.GeoData
|
||||
StoredGeoData = geo.StoredGeoData
|
||||
|
||||
@app.before_first_request
|
||||
|
|
|
@ -68,8 +68,6 @@ def merge_bboxes(a, b):
|
|||
|
||||
def find_bbox(data):
|
||||
|
||||
app.debugger.set_trace()
|
||||
|
||||
bbox = [180, -80, -180, 80]
|
||||
|
||||
if isinstance(data, geojson.FeatureCollection):
|
||||
|
@ -448,7 +446,7 @@ class StoredGeoDataForm(poobrains.auth.AutoForm):
|
|||
self.fields['data'].value = self.instance.data
|
||||
|
||||
else:
|
||||
|
||||
|
||||
if self.fields['data'].value.mimetype in ('application/json', 'application/geo+json'):
|
||||
|
||||
fd = self.fields['data'].value.stream
|
||||
|
|
|
@ -3,8 +3,9 @@ import math
|
|||
import time
|
||||
import datetime
|
||||
import json
|
||||
import pandas
|
||||
|
||||
from poobrains import Markup, app, request, abort, redirect, g, session, locked_cached_property
|
||||
from poobrains import Markup, app, request, abort, redirect, g, session, locked_cached_property, new_session
|
||||
|
||||
import poobrains.helpers
|
||||
import poobrains.errors
|
||||
|
@ -58,6 +59,7 @@ class Dataset(poobrains.auth.Protected):
|
|||
title = None # to override Renderable's @property
|
||||
description = poobrains.md.MarkdownString()
|
||||
|
||||
|
||||
def __init__(self, name=None, title=None, description=None, label_x=None, label_y=None):
|
||||
|
||||
self._name = name
|
||||
|
@ -66,13 +68,16 @@ class Dataset(poobrains.auth.Protected):
|
|||
self.label_x = label_x or 'X'
|
||||
self.label_y = label_y or 'Y'
|
||||
|
||||
self.datapoints = poobrains.helpers.TypedList(Datapoint)
|
||||
self.data = pandas.DataFrame()
|
||||
|
||||
|
||||
def __iadd__(self, value): # +=
|
||||
|
||||
for datapoint in value.datapoints:
|
||||
self.datapoints.append(datapoint)
|
||||
self.data += value # just let pandas do its thing
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return len(self.data)
|
||||
|
||||
|
||||
@property
|
||||
|
@ -85,88 +90,31 @@ class Dataset(poobrains.auth.Protected):
|
|||
self._name = value
|
||||
|
||||
|
||||
@property
|
||||
def authorized_datapoints(self):
|
||||
return self.datapoints
|
||||
|
||||
|
||||
@property
|
||||
def ref_id(self):
|
||||
return "dataset-%s" % self.name
|
||||
|
||||
|
||||
def datapoint_id(self, datapoint):
|
||||
return "dataset-%s-%s" % (self.name, datapoint.x)
|
||||
def datapoint_id(self, x):
|
||||
return "dataset-%s-%s" % (self.name, x)
|
||||
|
||||
|
||||
def render(self, mode=None):
|
||||
|
||||
if mode == 'json':
|
||||
return self.data.to_json()
|
||||
|
||||
return super(Dataset, self).render(mode=mode)
|
||||
|
||||
|
||||
def append(self, datapoint):
|
||||
self.datapoints.append(datapoint)
|
||||
|
||||
|
||||
def add_datapoint(self, x, y, error_lower=None, error_upper=None):
|
||||
|
||||
"""
|
||||
convenience function to append a datapoint.
|
||||
"""
|
||||
|
||||
datapoint = Datapoint(x, y, error_lower=error_lower, error_upper=error_upper)
|
||||
self.append(datapoint)
|
||||
|
||||
|
||||
def plot(self):
|
||||
return Plot(datasets=[self]).render('raw')
|
||||
|
||||
|
||||
def fill(self, *args):
|
||||
raise NotImplementedError("%s.fill not implemented" % type(self).__name__)
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
|
||||
d = {
|
||||
'name': self.name,
|
||||
'title': self.title,
|
||||
'description': self.description,
|
||||
'label_x': self.label_x,
|
||||
'label_y': self.label_y
|
||||
}
|
||||
|
||||
datapoints = []
|
||||
for datapoint in self.authorized_datapoints:
|
||||
datapoints.append({
|
||||
'x': datapoint.x,
|
||||
'y': datapoint.y,
|
||||
'error_lower': datapoint.error_lower,
|
||||
'error_upper': datapoint.error_upper
|
||||
})
|
||||
|
||||
d['datapoints'] = datapoints
|
||||
|
||||
return d
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d):
|
||||
|
||||
kwargs = {
|
||||
'name': d['name'],
|
||||
'title': d['title'],
|
||||
'description': d['description'],
|
||||
'label_x': d['label_x'],
|
||||
'label_y': d['label_y']
|
||||
}
|
||||
|
||||
ds = cls(**kwargs)
|
||||
for datapoint in d['datapoints']:
|
||||
ds.add_datapoint(datapoint['x'], datapoint['y'], error_lower=datapoint['error_lower'], error_upper=datapoint['error_upper'])
|
||||
|
||||
return ds
|
||||
|
||||
|
||||
def save(self, name=None, owner=None):
|
||||
|
||||
"""
|
||||
Convert this Dataset and all its Datapoints into StoredDataset and StoredDatapoints and save them all.
|
||||
Convert this Dataset into a StoredDataset and save it.
|
||||
"""
|
||||
|
||||
ds = StoredDataset()
|
||||
|
@ -180,27 +128,19 @@ class Dataset(poobrains.auth.Protected):
|
|||
ds.label_x = self.label_x
|
||||
ds.label_y = self.label_y
|
||||
ds.description = self.description
|
||||
ds.data = self.data
|
||||
ds.save(force_insert=True)
|
||||
|
||||
for datapoint in self.datapoints:
|
||||
ds.add_datapoint( # StoredDatapoint.add_datapoint implicitly saves the added datapoint
|
||||
datapoint.x,
|
||||
datapoint.y,
|
||||
error_lower=datapoint.error_lower,
|
||||
error_upper=datapoint.error_upper
|
||||
)
|
||||
|
||||
return ds
|
||||
|
||||
|
||||
class Datapoint(poobrains.auth.Protected):
|
||||
class StoredDatasetForm(poobrains.auth.AutoForm):
|
||||
|
||||
def __init__(self, x, y, error_lower=None, error_upper=None):
|
||||
data = poobrains.form.fields.File()
|
||||
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.error_lower = error_lower
|
||||
self.error_upper = error_upper
|
||||
def process(self, submit, exceptions=False):
|
||||
|
||||
self.instance.data = pandas.from_json(self.fields['data'].value)
|
||||
|
||||
|
||||
class StoredDataset(Dataset, poobrains.commenting.Commentable):
|
||||
|
@ -209,94 +149,37 @@ class StoredDataset(Dataset, poobrains.commenting.Commentable):
|
|||
description = poobrains.md.MarkdownField(null=True)
|
||||
label_x = poobrains.storage.fields.CharField(verbose_name="Label for the x-axis")
|
||||
label_y = poobrains.storage.fields.CharField(verbose_name="Label for the y-axis")
|
||||
data = poobrains.storage.fields.TextField()
|
||||
|
||||
form_add = StoredDatasetForm
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._data = None
|
||||
return super(Dataset, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name == 'data':
|
||||
if self._data is None:
|
||||
#self._data = pandas.DataFrame.from_dict(json.loads(self.data))
|
||||
self._data = pandas.DataFrame.from_dict(super(StoredDataset, self).__getattribute__('data'))
|
||||
return self._data
|
||||
return super(StoredDataset, self).__getattribute__(name)
|
||||
|
||||
|
||||
@property
|
||||
def ref_id(self):
|
||||
|
||||
return "dataset-%s" % self.name
|
||||
|
||||
|
||||
@locked_cached_property
|
||||
def authorized_datapoints(self):
|
||||
return StoredDatapoint.list('read', g.user).where(StoredDatapoint.dataset == self)
|
||||
|
||||
|
||||
def datapoint_id(self, datapoint):
|
||||
return "dataset-%s-%s" % (self.name, datapoint.x)
|
||||
|
||||
|
||||
def append(self, datapoint):
|
||||
|
||||
datapoint.dataset = self
|
||||
if not datapoint.owner_id:
|
||||
datapoint.owner = self.owner
|
||||
|
||||
return datapoint.save(force_insert=True)
|
||||
|
||||
|
||||
def add_datapoint(self, x, y, error_lower=None, error_upper=None):
|
||||
|
||||
"""
|
||||
convenience function to append a datapoint.
|
||||
"""
|
||||
|
||||
datapoint = StoredDatapoint()
|
||||
datapoint.x = x
|
||||
datapoint.y = y
|
||||
datapoint.error_lower=error_lower
|
||||
datapoint.error_upper=error_upper
|
||||
|
||||
self.append(datapoint)
|
||||
|
||||
|
||||
def save(self, **kwargs):
|
||||
return super(Dataset, self).save(**kwargs)
|
||||
|
||||
|
||||
class StoredDatapointFieldset(poobrains.form.Fieldset):
|
||||
|
||||
def __init__(self, datapoint, **kwargs):
|
||||
|
||||
super(StoredDatapointFieldset, self).__init__(**kwargs)
|
||||
|
||||
self.datapoint = datapoint
|
||||
self.x = poobrains.form.fields.Text(type=poobrains.form.types.FLOAT, value=self.datapoint.x, placeholder=StoredDatapoint.x.verbose_name, help_text=StoredDatapoint.x.help_text)
|
||||
self.y = poobrains.form.fields.Text(type=poobrains.form.types.FLOAT, value=self.datapoint.y, placeholder=StoredDatapoint.y.verbose_name, help_text=StoredDatapoint.y.help_text)
|
||||
self.error_lower = poobrains.form.fields.Text(type=poobrains.form.types.FLOAT, value=self.datapoint.error_lower, placeholder=StoredDatapoint.error_lower.verbose_name, help_text=StoredDatapoint.error_lower.help_text)
|
||||
self.error_upper = poobrains.form.fields.Text(type=poobrains.form.types.FLOAT, value=self.datapoint.error_upper, placeholder=StoredDatapoint.error_upper.verbose_name, help_text=StoredDatapoint.error_upper.help_text)
|
||||
|
||||
|
||||
def process(self, submit, dataset):
|
||||
|
||||
if self.datapoint in dataset.datapoints:
|
||||
pass # florp
|
||||
|
||||
else:
|
||||
|
||||
self.datapoint.dataset = dataset
|
||||
self.datapoint.x = self.fields['x'].value
|
||||
self.datapoint.y = self.fields['y'].value
|
||||
self.datapoint.error_lower = self.fields['error_lower'].value
|
||||
self.datapoint.error_upper = self.fields['error_upper'].value
|
||||
|
||||
self.datapoint.save(force_insert=True)
|
||||
|
||||
|
||||
class StoredDatapoint(poobrains.auth.Owned, Datapoint):
|
||||
|
||||
class Meta:
|
||||
order_by = ['dataset', 'x']
|
||||
primary_key = poobrains.storage.CompositeKey('dataset', 'x')
|
||||
related_use_form = True
|
||||
|
||||
dataset = poobrains.storage.fields.ForeignKeyField(StoredDataset, backref='datapoints')
|
||||
x = poobrains.storage.fields.DoubleField()
|
||||
y = poobrains.storage.fields.DoubleField()
|
||||
error_lower = poobrains.storage.fields.FloatField(help_text="Lower margin of error", null=True)
|
||||
error_upper = poobrains.storage.fields.FloatField(help_text="Upper margin of error", null=True)
|
||||
dataobj = self.data
|
||||
self.data = dataobj.to_json()
|
||||
r = super(Dataset, self).save(**kwargs)
|
||||
self.data = dataobj
|
||||
return r
|
||||
|
||||
|
||||
@app.expose('/svg/plot')
|
||||
|
@ -313,13 +196,16 @@ class Plot(base.SVG):
|
|||
|
||||
datasets = None
|
||||
length = None
|
||||
|
||||
# TODO: deprecate all this by just statically computed bounds?
|
||||
min_x = None
|
||||
max_x = None
|
||||
min_y = None
|
||||
max_y = None
|
||||
span_x = None
|
||||
span_y = None
|
||||
|
||||
|
||||
bounds = None
|
||||
|
||||
class Meta:
|
||||
|
||||
|
@ -366,67 +252,90 @@ class Plot(base.SVG):
|
|||
#for datapoint in Datapoint.list('read', g.user).where(Datapoint.dataset << self.datasets):
|
||||
for dataset in self.datasets:
|
||||
|
||||
l = len(dataset.authorized_datapoints)
|
||||
l = len(dataset)
|
||||
if l > self.length:
|
||||
self.length = l
|
||||
|
||||
for datapoint in dataset.authorized_datapoints:
|
||||
|
||||
datapoint_count += 1
|
||||
|
||||
y_lower = datapoint.y
|
||||
if datapoint.error_lower:
|
||||
y_lower -= datapoint.error_lower
|
||||
|
||||
y_upper = datapoint.y
|
||||
if datapoint.error_upper:
|
||||
y_upper += datapoint.error_upper
|
||||
|
||||
if self.min_x is None or datapoint.x < self.min_x:
|
||||
self.min_x = datapoint.x
|
||||
|
||||
if self.max_x is None or datapoint.x > self.max_x:
|
||||
self.max_x = datapoint.x
|
||||
|
||||
if self.min_y is None or y_lower < self.min_y:
|
||||
self.min_y = y_lower
|
||||
|
||||
if self.max_y is None or y_upper > self.max_y:
|
||||
self.max_y = y_upper
|
||||
|
||||
if datapoint_count > 0:
|
||||
self.span_x = self.max_x - self.min_x
|
||||
self.span_y = self.max_y - self.min_y
|
||||
|
||||
else:
|
||||
self.min_x = 0
|
||||
self.max_x = 0
|
||||
self.min_y = 0
|
||||
self.max_y = 0
|
||||
self.span_x = 0
|
||||
self.span_y = 0
|
||||
def is_dataframe(self, x):
|
||||
return type(x) is pandas.DataFrame
|
||||
|
||||
|
||||
def render(self, mode=None):
|
||||
@property
|
||||
def min_x(self):
|
||||
|
||||
if mode == 'json':
|
||||
mins = []
|
||||
for dataset in self.datasets:
|
||||
if isinstance(dataset.data, pandas.DataFrame):
|
||||
submins = []
|
||||
for subset_index in dataset.data:
|
||||
submins.append(min(dataset.data[subset_index].index))
|
||||
|
||||
data = {}
|
||||
mins.append(min(submins))
|
||||
|
||||
for dataset in self.datasets:
|
||||
data[dataset.name] = []
|
||||
else:
|
||||
mins.append(min(dataset.data.index))
|
||||
|
||||
for datapoint in dataset.authorized_datapoints:
|
||||
data[dataset.name].append({
|
||||
'x': datapoint.x,
|
||||
'y': datapoint.y,
|
||||
'error_lower': datapoint.error_lower,
|
||||
'error_upper': datapoint.error_upper
|
||||
})
|
||||
return min(mins)
|
||||
|
||||
return Markup(json.dumps(data))
|
||||
|
||||
return super(Plot, self).render(mode=mode)
|
||||
@property
|
||||
def max_x(self):
|
||||
|
||||
maxes = []
|
||||
|
||||
for dataset in self.datasets:
|
||||
|
||||
if isinstance(dataset.data, pandas.DataFrame):
|
||||
submaxes = []
|
||||
for subset_index in dataset.data:
|
||||
submaxes.append(max(dataset.data[subset_index].index))
|
||||
|
||||
maxes.append(max(submaxes))
|
||||
|
||||
else:
|
||||
|
||||
maxes.append(max(dataset.data.index))
|
||||
|
||||
return max(maxes)
|
||||
|
||||
|
||||
@property
|
||||
def min_y(self):
|
||||
mins = []
|
||||
for dataset in self.datasets:
|
||||
if isinstance(dataset.data, pandas.DataFrame):
|
||||
submins = []
|
||||
for subset_index in dataset.data:
|
||||
submins.append(min(dataset.data[subset_index]))
|
||||
|
||||
mins.append(min(submins))
|
||||
|
||||
else:
|
||||
mins.append(min(dataset.data))
|
||||
|
||||
return min(mins)
|
||||
|
||||
|
||||
@property
|
||||
def max_y(self):
|
||||
|
||||
maxes = []
|
||||
|
||||
for dataset in self.datasets:
|
||||
|
||||
if isinstance(dataset.data, pandas.DataFrame):
|
||||
submaxes = []
|
||||
for subset_index in dataset.data:
|
||||
submaxes.append(max(dataset.data[subset_index]))
|
||||
|
||||
maxes.append(max(submaxes))
|
||||
|
||||
else:
|
||||
|
||||
maxes.append(max(dataset))
|
||||
|
||||
return max(maxes)
|
||||
|
||||
|
||||
def normalize_x(self, value):
|
||||
|
@ -457,6 +366,88 @@ class Plot(base.SVG):
|
|||
return u' / '.join([dataset.label_y for dataset in self.datasets])
|
||||
|
||||
|
||||
def dataset_griddable(self, dataset):
|
||||
|
||||
valid_dtypes = [
|
||||
int,
|
||||
float
|
||||
]
|
||||
|
||||
for dtype in valid_dtypes:
|
||||
|
||||
x = False
|
||||
y = False
|
||||
|
||||
if dataset.data.index.dtype in valid_dtypes:
|
||||
x = True # x axis is numerical, can be gridded
|
||||
|
||||
if dtype in dataset.data.dtypes.values:
|
||||
y = True # y axais is numeric, can be gridded
|
||||
|
||||
if x and y:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@property
|
||||
def griddable_datasets(self):
|
||||
|
||||
datasets = []
|
||||
for ds in self.datasets:
|
||||
if self.dataset_griddable(ds):
|
||||
datasets.append(ds)
|
||||
|
||||
return datasets
|
||||
|
||||
|
||||
@property
|
||||
def has_grid(self):
|
||||
|
||||
for ds in self.datasets:
|
||||
if self.dataset_griddable(ds):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@property
|
||||
def span_x(self):
|
||||
|
||||
absolute_minimum = None
|
||||
absolute_maximum = None
|
||||
|
||||
for ds in self.griddable_datasets:
|
||||
|
||||
local_minimum = min(ds.data.index)
|
||||
local_maximum = max(ds.data.index)
|
||||
if absolute_minimum is None or local_minimum < absolute_minimum:
|
||||
absolute_minimum = local_minimum
|
||||
|
||||
if absolute_maximum is None or local_maximum > absolute_maximum:
|
||||
absolute_maximum = local_maximum
|
||||
|
||||
return absolute_maximum - absolute_minimum
|
||||
|
||||
|
||||
@property
|
||||
def span_y(self):
|
||||
|
||||
absolute_minimum = None
|
||||
absolute_maximum = None
|
||||
|
||||
for ds in self.griddable_datasets:
|
||||
for column in ds.data.columns:
|
||||
for value in ds.data[column].values:
|
||||
if absolute_minimum is None or value < absolute_minimum:
|
||||
absolute_minimum = value
|
||||
if absolute_maximum is None or value > absolute_maximum:
|
||||
absolute_maximum = value
|
||||
|
||||
if not absolute_minimum is None:
|
||||
return absolute_maximum - absolute_minimum
|
||||
|
||||
|
||||
@property
|
||||
def grid_x(self):
|
||||
|
||||
|
@ -479,7 +470,7 @@ class Plot(base.SVG):
|
|||
|
||||
@property
|
||||
def grid_y(self):
|
||||
#app.debugger.set_trace()
|
||||
|
||||
if self.span_y == 0:
|
||||
return [self.min_y]
|
||||
|
||||
|
@ -511,6 +502,11 @@ class BarChart(Plot):
|
|||
return (self.plot_width - self.padding) / self.length - 1
|
||||
|
||||
|
||||
@new_session
|
||||
def editor_session(session):
|
||||
session['editor-sessions'] = {}
|
||||
|
||||
|
||||
def new_editor_handle():
|
||||
|
||||
handle = poobrains.helpers.random_string_light()
|
||||
|
@ -523,7 +519,6 @@ def new_editor_handle():
|
|||
|
||||
def dataset_choices():
|
||||
|
||||
|
||||
dynamic = []
|
||||
for name, cls in dynamic_datasets().items():
|
||||
|
||||
|
@ -549,10 +544,9 @@ class EditorLoadFieldset(poobrains.form.Fieldset):
|
|||
|
||||
|
||||
def process(self, submit, instance):
|
||||
|
||||
if submit == '%s.%s' % (self.name, 'load_dataset'):
|
||||
instance.data = load_dataset(self.fields['dataset'].value)
|
||||
session[instance.handle_string] = instance.data.to_dict()
|
||||
instance.dataset = load_dataset(self.fields['dataset'].value)
|
||||
session['editor-sessions'][instance.handle_string] = instance.dataset.data.to_dict()
|
||||
|
||||
|
||||
@app.expose('/svg/plot/editor/', force_secure=True)
|
||||
|
@ -565,8 +559,7 @@ class DataEditor(poobrains.auth.ProtectedForm):
|
|||
|
||||
super(DataEditor, self).__init__(handle=handle, mode=mode, **kwargs)
|
||||
|
||||
self.data = None
|
||||
|
||||
self.dataset = None
|
||||
#self.fields['load_fieldset'] = EditorLoadFieldset(prefix=self.name, name='load_fieldset')
|
||||
self.add_field(EditorLoadFieldset())
|
||||
|
||||
|
@ -578,22 +571,21 @@ class DataEditor(poobrains.auth.ProtectedForm):
|
|||
return redirect(type(self)(handle=new_editor_handle()).url('full'))
|
||||
|
||||
if self.handle_string in session:
|
||||
self.data = Dataset.from_dict(session[self.handle_string])
|
||||
#self.data = session[self.handle_string]
|
||||
#self.dataset = session[self.handle_string]
|
||||
self.dataset = Dataset()
|
||||
self.dataset.data = pandas.DataFrame.from_dict(session['editor-sessions'][self.handle_string])
|
||||
|
||||
return poobrains.helpers.ThemedPassthrough(super(DataEditor, self).view(mode=mode, **kwargs))
|
||||
|
||||
|
||||
def process(self, submit):
|
||||
|
||||
app.debugger.set_trace()
|
||||
|
||||
if submit.startswith('EditorLoadFieldset.'):
|
||||
self.fields['EditorLoadFieldset'].process(submit, self)
|
||||
#session[self.handle_string] = self.data
|
||||
|
||||
elif submit == 'save_dataset':
|
||||
stored_ds = self.data.save()
|
||||
stored_ds = self.dataset.save()
|
||||
return redirect(stored_ds.url('edit'))
|
||||
|
||||
return self
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
{% extends 'form/form.jinja' %}
|
||||
|
||||
{% block pre %}
|
||||
{% if content.data %}
|
||||
{{ content.data.plot() }}
|
||||
{% if content.dataset %}
|
||||
<div class="dataeditor-plot">
|
||||
{{ content.dataset.plot() }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
<form
|
||||
id="{{ content.ref_id }}"
|
||||
class="content-type-{{ content.__class__.__name__.lower() }} mode-{{ mode }} {{ content.css_class }}"
|
||||
method="{{ content.method }}"
|
||||
{% if content.action %}action="{{ content.action }}"{% endif %}
|
||||
enctype="multipart/form-data" >
|
||||
|
|
|
@ -1317,5 +1317,10 @@ object svg.plot {
|
|||
|
||||
}
|
||||
|
||||
.dataeditor-plot {
|
||||
background: $color_background_dark;
|
||||
}
|
||||
.content-type-dataeditor {}
|
||||
|
||||
@import 'form';
|
||||
@import 'custom';
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
{# this just exists, because otherwise commentable.jinja is used #}
|
||||
{% extends "geodata-raw.jinja" %}
|
|
@ -0,0 +1,5 @@
|
|||
{% extends "commentable.jinja" %}
|
||||
|
||||
{% block content %}
|
||||
<object class="svg-wrapper" data="{{ content.map_url }}"></object>
|
||||
{% endblock %}
|
|
@ -11,7 +11,7 @@
|
|||
>
|
||||
|
||||
<!-- a json representation of all visualized data, not used since there's a strict nojs policy, but included for your convenience -->
|
||||
{{ content.render('json') }}
|
||||
{# content.render('json') #}
|
||||
|
||||
<style>
|
||||
{{ content.style }}
|
||||
|
@ -23,42 +23,45 @@
|
|||
<text class="axis-label axis-y" transform="rotate(-90 0 {{ content.plot_height + content.padding }}) translate(5 15)" x="0" y="{{ content.plot_height + content.padding }}">{{ content.label_y }}</text>
|
||||
|
||||
<svg class="plot-inner" x="{{ content.padding }}" y="{{ content.padding }}" width="{{ content.inner_width }}" height="{{ content.inner_height }}" viewBox="0 0 {{ content.inner_width }} {{ content.inner_height }}" preserveAspectRatio="xMinYMin meet">
|
||||
<g class="grid">
|
||||
{% block grid %}
|
||||
{% for i in range(0, content.grid_x|length) %}
|
||||
|
||||
{% set x = content.grid_x[i] %}
|
||||
{% if content.has_grid %}
|
||||
<g class="grid">
|
||||
{% block grid %}
|
||||
{% for i in range(0, content.grid_x|length) %}
|
||||
|
||||
{% if content.grid_x|length >= 4 %}
|
||||
{% set highlighted = i % (content.grid_x|length / 4.0)|int == 0 %} {# whether this line should be highlighted and have a value shown next to it #}
|
||||
{% else %}
|
||||
{% set highlighted = True %}
|
||||
{% endif %}
|
||||
{% set x = content.grid_x[i] %}
|
||||
|
||||
<line class="grid-x{% if highlighted %} highlighted{% endif %}" x1="{{ content.normalize_x(x) }}" y1="0%" x2="{{ content.normalize_x(x) }}" y2="{{ content.plot_height }}" />
|
||||
|
||||
{% if highlighted %}
|
||||
<text class="grid-label axis-x" x="{{ content.normalize_x(x) }}" y="{{ content.plot_height }}" dominant-baseline="hanging">{{ "%.3f"|format(x) }}</text>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if content.grid_x|length >= 4 %}
|
||||
{% set highlighted = i % (content.grid_x|length / 4.0)|int == 0 %} {# whether this line should be highlighted and have a value shown next to it #}
|
||||
{% else %}
|
||||
{% set highlighted = True %}
|
||||
{% endif %}
|
||||
|
||||
{% for i in range(0, content.grid_y|length) %}
|
||||
{% set y = content.grid_y[i] %}
|
||||
|
||||
{% if content.grid_y|length >= 4 %}
|
||||
{% set highlighted = i % (content.grid_y|length / 4)|int == 0 %} {# whether this line should be highlighted and have a value shown next to it #}
|
||||
{% else %}
|
||||
{% set highlighted = True %}
|
||||
{% endif %}
|
||||
<line class="grid-x{% if highlighted %} highlighted{% endif %}" x1="{{ content.normalize_x(x) }}" y1="0%" x2="{{ content.normalize_x(x) }}" y2="{{ content.plot_height }}" />
|
||||
|
||||
{% if highlighted %}
|
||||
<text class="grid-label axis-x" x="{{ content.normalize_x(x) }}" y="{{ content.plot_height }}" dominant-baseline="hanging">{{ "%.3f"|format(x) }}</text>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% for i in range(0, content.grid_y|length) %}
|
||||
{% set y = content.grid_y[i] %}
|
||||
|
||||
{% if content.grid_y|length >= 4 %}
|
||||
{% set highlighted = i % (content.grid_y|length / 4)|int == 0 %} {# whether this line should be highlighted and have a value shown next to it #}
|
||||
{% else %}
|
||||
{% set highlighted = True %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
<line class="grid-y{% if highlighted %} highlighted{% endif %}" x1="0%" y1="{{ content.normalize_y(y) }}" x2="100%" y2="{{ content.normalize_y(y) }}" />
|
||||
{% if highlighted %}
|
||||
<text class="grid-label axis-y" transform="rotate(-90 0 {{ content.normalize_y(y) }}) translate(5 -15)" x="0" y="{{ content.normalize_y(y) }}" dominant-baseline="hanging">{{ "%.3f"|format(y) }}</text>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
</g>
|
||||
<line class="grid-y{% if highlighted %} highlighted{% endif %}" x1="0%" y1="{{ content.normalize_y(y) }}" x2="100%" y2="{{ content.normalize_y(y) }}" />
|
||||
{% if highlighted %}
|
||||
<text class="grid-label axis-y" transform="rotate(-90 0 {{ content.normalize_y(y) }}) translate(5 -15)" x="0" y="{{ content.normalize_y(y) }}" dominant-baseline="hanging">{{ "%.3f"|format(y) }}</text>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
</g>
|
||||
{% endif %}
|
||||
|
||||
<g class="datasets">
|
||||
{% block datasets scoped %}
|
||||
|
@ -70,40 +73,39 @@
|
|||
|
||||
{% block dataset scoped %}
|
||||
|
||||
{% for datapoint in dataset.authorized_datapoints %}
|
||||
{% if content.is_dataframe(dataset.data) %}
|
||||
{% for series_index in dataset.data %}
|
||||
{% set series = dataset.data[series_index] %}
|
||||
|
||||
{% for x, y in series.iteritems() %}
|
||||
|
||||
<a href="#{{ dataset.datapoint_id(datapoint) }}">
|
||||
<g id="{{ dataset.datapoint_id(datapoint) }}" class="datapoint">
|
||||
{% block datapoint scoped %}
|
||||
<a href="#{{ dataset.datapoint_id(x) }}">
|
||||
<g id="{{ dataset.datapoint_id(x) }}" class="datapoint">
|
||||
{% block datapoint scoped %}
|
||||
|
||||
<g class="error">
|
||||
<use href="#marker" class="marker" x="{{ content.normalize_x(x) }}" y="{{ content.normalize_y(y) }}" />
|
||||
|
||||
{% if datapoint.error_upper %}
|
||||
<g class="upper">
|
||||
<line x1="{{ content.normalize_x(datapoint.x) }}" y1="{{ content.normalize_y(datapoint.y) }}" x2="{{ content.normalize_x(datapoint.x) }}" y2="{{ content.normalize_y(datapoint.y + datapoint.error_upper) }}" />
|
||||
<line x1="{{ content.normalize_x(datapoint.x) - 5 }}" y1="{{ content.normalize_y(datapoint.y + datapoint.error_upper) }}" x2="{{ content.normalize_x(datapoint.x) + 5 }}" y2="{{ content.normalize_y(datapoint.y + datapoint.error_upper) }}" />
|
||||
<text class="datapoint-value" x="{{ content.normalize_x(x) + 5 }}" y="{{ content.normalize_y(y) - 20 }}">{{ "%.5f"|format(y) }}</text>
|
||||
|
||||
{% endblock %}
|
||||
</g>
|
||||
{% endif %}
|
||||
</a>
|
||||
|
||||
{% if datapoint.error_lower %}
|
||||
<g class="lower">
|
||||
<line x1="{{ content.normalize_x(datapoint.x) }}" y1="{{ content.normalize_y(datapoint.y) }}" x2="{{ content.normalize_x(datapoint.x) }}" y2="{{ content.normalize_y(datapoint.y - datapoint.error_lower) }}" />
|
||||
<line x1="{{ content.normalize_x(datapoint.x) - 5 }}" y1="{{ content.normalize_y(datapoint.y - datapoint.error_lower) }}" x2="{{ content.normalize_x(datapoint.x) + 5 }}" y2="{{ content.normalize_y(datapoint.y - datapoint.error_lower) }}" />
|
||||
</g>
|
||||
{% endif %}
|
||||
|
||||
</g>
|
||||
|
||||
<use href="#marker" class="marker" x="{{ content.normalize_x(datapoint.x) }}" y="{{ content.normalize_y(datapoint.y) }}" />
|
||||
|
||||
<text class="datapoint-value" x="{{ content.normalize_x(datapoint.x) + 5 }}" y="{{ content.normalize_y(datapoint.y) - 20 }}">{{ "%.5f"|format(datapoint.y) }}</text>
|
||||
|
||||
{% endblock %}
|
||||
</g>
|
||||
</a>
|
||||
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
|
||||
{% for datapoint in dataset %}
|
||||
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
<a class="dataset-link" href="#{{ dataset.ref_id }}">
|
||||
<text class="dataset-name" x="{{ content.inner_width }}" y="{{ 10 + (idx * 20) }}" text-anchor="end">{{ dataset.title }}</text>
|
||||
|
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
@ -1,9 +1,8 @@
|
|||
{% extends 'renderable.jinja' %}
|
||||
|
||||
{% block content %}
|
||||
{# <object class="svg-wrapper" data="{{ content.url('raw') }}" ></object> #}
|
||||
|
||||
{{ content.render('raw') }}
|
||||
<object class="svg-wrapper" data="{{ content.url('raw') }}" ></object>
|
||||
<a href="{{ content.url('raw') }}" target="_blank">Save</a>
|
||||
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in New Issue