Procházet zdrojové kódy

Might contain traces of Progress™.

master
User Phryk před 5 měsíci
rodič
revize
7a7fe7ce70
5 změnil soubory, kde provedl 294 přidání a 338 odebrání
  1. +1
    -1
      poobrains/form/__init__.py
  2. +247
    -279
      poobrains/svg/plot.py
  3. +12
    -16
      poobrains/themes/default/form/dataeditor.jinja
  4. +32
    -42
      poobrains/themes/default/svg/plot-raw.jinja
  5. +2
    -0
      setup.py

+ 1
- 1
poobrains/form/__init__.py Zobrazit soubor

@@ -389,7 +389,7 @@ class Fieldset(BaseForm):
readonly = None
rendered = None
multi = False
_default = werkzeug.MultiDict()
_default = werkzeug.datastructures.MultiDict()

class Meta:
abstract = True


+ 247
- 279
poobrains/svg/plot.py Zobrazit soubor

@@ -2,8 +2,8 @@ import collections
import math
import time
import datetime
#import json
import pickle
import json
#import pickle
import inspect
import pandas

@@ -63,26 +63,71 @@ def load_dataset(handle):
return ds


def dataset_choices():

dynamic = []
for name, cls in dynamic_datasets().items():

try:
cls.permissions['read'].check(g.user)
name = '_%s' % name
dynamic.append((name, cls.__name__))
except poobrains.auth.AccessDenied:
pass

stored = []
for dataset in StoredDataset.list('read', g.user):
stored.append((dataset.name, dataset.title))

choices = [(dynamic, 'Dynamic Datasets'), (stored, 'Stored Datasets')]
return choices


def dynamic_dataset_parameters(dataset_class):

r = {}
signature = inspect.signature(dataset_class.fill)
for param in signature.parameters.values():

if param.name == 'self':
continue # next loop iteration

if param.annotation == param.empty:
param_type = str

else:
param_type = param.annotation

r[param.name] = {
'type': param_type,
'default': param.default
}

return r


def validate_handle_not_in_session(handle):
if handle in session['editor-sessions']:
raise poobrains.errors.ValidationError("Editor Session named '%s' already exists!" % handle)
#

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, plot_kind='scatter'):
def __init__(self, name=None, title=None, description=None, data=None, label_x=None, label_y=None):

self._name = name
self.title = title or type(self).__name__
self.description = description or ''
self.label_x = label_x or 'X'
self.label_y = label_y or 'Y'
self.plot_kind = plot_kind

self.data = pandas.DataFrame()


def __iadd__(self, value): # +=

self.data += value # just let pandas do its thing
if not data is None:
self.data = data
else:
self.data = pandas.DataFrame()


def __len__(self):
@@ -116,15 +161,12 @@ class Dataset(poobrains.auth.Protected):
return super(Dataset, self).render(mode=mode)

def plot(self, kind=None):

if kind is None:
kind = self.plot_kind
def plot(self, kind='scatter'):

if not kind in plot_kinds:
raise ValueError('wrong plot kind: %s' % kind)

return plot_kinds[kind](datasets=[self]).render('raw')
return plot_kinds[kind](dataset=self).render('raw')


def save(self, name=None, owner=None):
@@ -145,19 +187,96 @@ class Dataset(poobrains.auth.Protected):
ds.label_y = self.label_y
ds.description = self.description
ds.data = self.data
ds.plot_kind = self.plot_kind
ds.save(force_insert=True)

return ds


#class StoredDatasetForm(poobrains.auth.AutoForm):
#
# data = poobrains.form.fields.File()
#
# def process(self, submit, exceptions=False):
#
# self.instance.data = pandas.from_json(self.fields['data'].value)

class EditorNewSessionFieldset(poobrains.form.Fieldset):

handle = poobrains.form.fields.Text(required=True, validators=[validate_handle_not_in_session])
new_session = poobrains.form.Button(label='Create', type='submit')


def process(self, submit, instance):

app.debugger.set_trace()

handle = self.fields['handle'].value

instance.owner = g.user
instance.name = handle
instance.title = handle.capitalize()
instance.label_x = 'x'
instance.label_y = 'y'
instance.save()

return instance.url('edit')


class EditorLoadFieldset(poobrains.form.Fieldset):

key = poobrains.form.fields.Text()
dataset = poobrains.form.fields.Select(choices=dataset_choices)
load = poobrains.form.Button(label='Load', type='submit')


def process(self, submit, instance):

app.debugger.set_trace()
ds = load_dataset(self.fields['dataset'].value)
session['editor-sessions'][instance.name]['data'] = session['editor-sessions'][instance.name]['data'].append(ds.data)



class DataEditor(poobrains.auth.BoundForm):

def __init__(self, instance, mode='add'):

super().__init__(instance, mode=mode)

app.debugger.set_trace()
if mode == 'add':
self.new = EditorNewSessionFieldset()

else:

if not self.instance.name in session['editor-sessions']:

# set up a fresh editor session holding current data
editor_session = {
'handle': self.instance.name,
'action': None,
'action_data': {},
'title': self.instance.title,
'description': self.instance.description,
'data': self.instance.data,
}

session['editor-sessions'][self.instance.name] = editor_session

else:
editor_session = session['editor-sessions'][self.instance.name]
self.load = EditorLoadFieldset(self.handle_string)

if not editor_session['data'].empty:
self.plot = Plot(dataset=Dataset(data=editor_session['data']))


def process(self, submit):

app.debugger.set_trace()

if submit.startswith('new.'):
self.fields['new'].process(submit, self.instance)

elif submit.startswith('load.'):
self.fields['load'].process(submit, self.instance)

return self


class StoredDataset(Dataset, poobrains.commenting.Commentable):
@@ -166,11 +285,11 @@ 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()
data = poobrains.storage.fields.BlobField(form_widget=None)
plot_kind = poobrains.storage.fields.CharField(default='scatter')
data = poobrains.storage.fields.TextField() # Fuck it, just use JSON
#data = poobrains.storage.fields.BlobField(form_widget=None)

#form_add = StoredDatasetForm
form_add = DataEditor
form_edit = DataEditor

def __init__(self, *args, **kwargs):

@@ -181,14 +300,14 @@ class StoredDataset(Dataset, poobrains.commenting.Commentable):
def __getattribute__(self, name):

if name == 'data':
#app.debugger.set_trace()
if self._data is None:
#self._data = pandas.DataFrame.from_dict(json.loads(self.data))
#self._data = pandas.DataFrame.from_dict(json.loads(super(StoredDataset, self).__getattribute__('data')))
#self._data = super(StoredDataset, self).__getattribute__('data')
#self._data = pandas.DataFrame.from_dict(json.loads(self.__data__['data']))
self._data = pickle.loads(self.__data__['data'])
#self._data = pickle.loads(super(StoredDataset, self).__getattribute__('data'))
if 'data' in self.__data__:
self._data = pandas.DataFrame.from_dict(json.loads(self.__data__['data']))
else:
self._data = pandas.DataFrame()
self.__data__['data'] = self._data.to_dict()
return self._data
return super(StoredDataset, self).__getattribute__(name)

@@ -198,7 +317,9 @@ class StoredDataset(Dataset, poobrains.commenting.Commentable):
if name == 'data':

if type(value) == pandas.DataFrame:
value = pickle.dumps(value)
#value = pickle.dumps(value)
#value = json.dumps(value)
value = value.to_json()

super(StoredDataset, self).__setattr__(name, value)

@@ -218,6 +339,16 @@ class StoredDataset(Dataset, poobrains.commenting.Commentable):
return r


# def view(self, mode='teaser', handle=None, **kwargs):
#
# if mode in ['add', 'edit']:
#
# editor = DataEditor()
# return editor.view('full')
#
# super().view(mode=mode, handle=handle, **kwargs)


@app.expose('/svg/plot')
class Plot(base.SVG):

@@ -230,7 +361,7 @@ class Plot(base.SVG):
plot_height = None
description_height = None

datasets = None
dataset = None
length = None

# TODO: deprecate all this by just statically computed bounds?
@@ -253,12 +384,14 @@ class Plot(base.SVG):
('inline', 'read')
])

def __init__(self, handle=None, mode=None, datasets=None, **kwargs):
def __init__(self, handle=None, mode=None, dataset=None, **kwargs):

super(Plot, self).__init__(handle=handle, mode=mode, **kwargs)

if handle is None and datasets is None:
abort(404, "No datasets selected")
app.debugger.set_trace()

if handle is None and dataset is None:
abort(404, "No dataset selected")
self.padding = app.config['SVG_PLOT_PADDING']
self.plot_width = app.config['SVG_PLOT_WIDTH']
@@ -269,28 +402,11 @@ class Plot(base.SVG):
self.inner_width = self.width - (2 * self.padding)
self.inner_height = self.height - (2 * self.padding)

if datasets:
self.datasets = datasets
if not dataset is None:
self.dataset = dataset

else:

self.datasets = []

dataset_names = handle.split(',')

for name in dataset_names:

ds = load_dataset(name)
self.datasets.append(ds)

datapoint_count = 0
self.length = 0
#for datapoint in Datapoint.list('read', g.user).where(Datapoint.dataset << self.datasets):
for dataset in self.datasets:

l = len(dataset)
if l > self.length:
self.length = l
self.dataset = load_dataset(handle)


def is_dataframe(self, x):
@@ -318,16 +434,15 @@ class Plot(base.SVG):
def min_x(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].index))
if isinstance(self.dataset.data, pandas.DataFrame):
submins = []
for subset_index in self.dataset.data:
submins.append(min(self.dataset.data[subset_index].index))

mins.append(min(submins))
mins.append(min(submins))

else:
mins.append(min(dataset.data.index))
else:
mins.append(min(self.dataset.data.index))

return min(mins)

@@ -337,35 +452,19 @@ class Plot(base.SVG):

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))
for subset_index in self.dataset.data:
maxes.append(max(self.dataset.data[subset_index].index))

return max(maxes)


@locked_cached_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))
mins = []

else:
mins.append(min(dataset.data))
for subset_index in self.dataset.data:
mins.append(min(self.dataset.data[subset_index]))

return min(mins)

@@ -375,18 +474,8 @@ class Plot(base.SVG):

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))
for subset_index in self.dataset.data:
maxes.append(max(self.dataset.data[subset_index]))

return max(maxes)

@@ -410,16 +499,19 @@ class Plot(base.SVG):
@locked_cached_property
def label_x(self):

return u' / '.join([dataset.label_x for dataset in self.datasets])
#return u' / '.join([dataset.label_x for dataset in self.datasets])
return self.dataset.label_x


@locked_cached_property
def label_y(self):

return u' / '.join([dataset.label_y for dataset in self.datasets])
#return u' / '.join([dataset.label_y for dataset in self.datasets])
return self.dataset.label_y


def dataset_griddable(self, dataset):
@locked_cached_property
def has_grid(self):

#app.debugger.set_trace()
valid_dtypes = [
@@ -432,10 +524,10 @@ class Plot(base.SVG):
x = False
y = False

if dataset.data.index.dtype in valid_dtypes:
if self.dataset.data.index.dtype in valid_dtypes:
x = True # x axis is numerical, can be gridded

if dtype in dataset.data.dtypes.values:
if dtype in self.dataset.data.dtypes.values:
y = True # y axais is numeric, can be gridded

if x and y:
@@ -444,65 +536,30 @@ class Plot(base.SVG):
return False


@locked_cached_property
def griddable_datasets(self):

datasets = []
for ds in self.datasets:
if self.dataset_griddable(ds):
datasets.append(ds)

return datasets


@locked_cached_property
def has_grid(self):

for ds in self.datasets:
if self.dataset_griddable(ds):
return True

return False


@locked_cached_property
def span_x(self):

if len(self.griddable_datasets) == 0:
return 0
if not self.has_grid:
return 0 # why, tho?

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
return max(self.dataset.data.index) - min(self.dataset.data.index)


@locked_cached_property
def span_y(self):

if len(self.griddable_datasets) == 0:
return 0
if not self.has_grid:
return 0 # why, tho?

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
for column in self.dataset.data.columns:
for value in self.dataset.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
@@ -575,90 +632,30 @@ def editor_session(session):
session['editor-sessions'] = {}


def new_editor_handle():

handle = poobrains.helpers.random_string_light()

if handle not in session['editor-sessions']:
return handle

return new_editor_handle()


def dataset_choices():

dynamic = []
for name, cls in dynamic_datasets().items():

try:
cls.permissions['read'].check(g.user)
name = '_%s' % name
dynamic.append((name, cls.__name__))
except poobrains.auth.AccessDenied:
pass

stored = []
for dataset in StoredDataset.list('read', g.user):
stored.append((dataset.name, dataset.title))

choices = [(dynamic, 'Dynamic Datasets'), (stored, 'Stored Datasets')]
return choices

def dynamic_dataset_parameters(dataset_class):

r = {}
signature = inspect.signature(dataset_class.fill)
for param in signature.parameters.values():

if param.name == 'self':
continue # next loop iteration

if param.annotation == param.empty:
param_type = str

else:
param_type = param.annotation

r[param.name] = {
'type': param_type,
'default': param.default
}

return r


def validate_handle_not_in_session(handle):
if handle in session['editor-sessions']:
raise poobrains.errors.ValidationError("Editor Session named '%s' already exists!" % handle)


class EditorNewSessionFieldset(poobrains.form.Fieldset):

handle = poobrains.form.fields.Text(required=True, validators=[validate_handle_not_in_session])
new_session = poobrains.form.Button(label='Create session', type='submit')

def __init__(self, editor_handle, **kwargs):

self.fields['handle'].default = editor_handle
self.fields['handle'].value = editor_handle
super(EditorNewSessionFieldset, self).__init__(**kwargs)


def process(self, submit, instance):

handle = self.fields['handle'].value

session['editor-sessions'][handle] = {
'handle': handle,
'action': None,
'action_data': {},
'datasets': {},
}

return redirect(DataEditor.url(handle=handle, mode='full'))

#class PlotKindFieldset(poobrains.form.Fieldset):
#
# def __init__(self, editor_handle, **kwargs):
# super(PlotKindFieldset, self).__init__(**kwargs)
#
# self.editor_handle = editor_handle
# action = session['editor-sessions'][self.editor_handle]['action']
#
# self.kind = poobrains.form.fields.Select(choices=[(kind, cls.__name__) for (kind, cls) in plot_kinds.items()], default=session['editor-sessions'][editor_handle]['plot_kind'])
#
# self.apply = poobrains.form.Button(type='submit', label='Apply')
#
#
# def process(self, submit, instance):
# session['editor-sessions'][instance.handle_string]['plot_kind'] = self.fields['kind'].value
# instance.dataset.plot_kind = self.fields['kind'].value

class EditorLoadFieldset(poobrains.form.Fieldset):
class EditorLoadFieldset_old(poobrains.form.Fieldset):

title = "Add new dataset to session"

@@ -699,6 +696,7 @@ class EditorLoadFieldset(poobrains.form.Fieldset):
else:
flash('Unknown dynamic dataset: %s' % dataset_name, 'error')


def summon_parameter_fieldset(self, dataset_class):

subform = poobrains.form.Fieldset(name='dynamic')
@@ -745,9 +743,9 @@ class EditorLoadFieldset(poobrains.form.Fieldset):
dataset.name = self.fields['dataset_name'].value
instance.datasets[dataset.name] = dataset
session['editor-sessions'][instance.handle_string]['action'] = None
session['editor-sessions'][instance.handle_string]['datasets'][dataset.name]['title'] = dataset.title
session['editor-sessions'][instance.handle_string]['datasets'][dataset.name]['description'] = dataset.description
session['editor-sessions'][instance.handle_string]['datasets'][dataset.name]['data'] = dataset.data.to_dict()
session['editor-sessions'][instance.handle_string]['data'][dataset.name]['title'] = dataset.title
session['editor-sessions'][instance.handle_string]['data'][dataset.name]['description'] = dataset.description
session['editor-sessions'][instance.handle_string]['data'][dataset.name]['data'] = dataset.data.to_dict()
except StoredDataset.DoesNotExist:
flash('Unknown stored dataset: %s' % dataset_name, 'error')

@@ -778,7 +776,7 @@ class EditorLoadFieldset(poobrains.form.Fieldset):
dataset.description = self.fields['dataset_description'].value

dataset.permissions['read'].check(g.user)
instance.datasets[dataset.name] = dataset
instance.dataset = dataset
session['editor-sessions'][instance.handle_string]['action'] = None
session['editor-sessions'][instance.handle_string]['datasets'][dataset.name] = {
'title': dataset.title,
@@ -792,28 +790,8 @@ class EditorLoadFieldset(poobrains.form.Fieldset):
else:
flash('Incomplete parameters for dynamic dataset %s!' % dataset_name, 'error')


class PlotKindFieldset(poobrains.form.Fieldset):

def __init__(self, editor_handle, **kwargs):
super(PlotKindFieldset, self).__init__(**kwargs)

self.editor_handle = editor_handle
action = session['editor-sessions'][self.editor_handle]['action']

k = next(iter(session['editor-sessions'][self.editor_handle]['datasets']))
default_plot_kind = session['editor-sessions'][self.editor_handle]['datasets'][k]['plot_kind'] # FIXME: Implement multiple plot modes within Plot
self.kind = poobrains.form.fields.Select(choices=[(kind, cls.__name__) for (kind, cls) in plot_kinds.items()], default=default_plot_kind)
self.apply = poobrains.form.Button(type='submit', label='Apply')


def process(self, submit, instance):
session['editor-sessions'][instance.handle_string]['plot_kind'] = self.fields['kind'].value
instance.dataset.plot_kind = self.fields['kind'].value


@app.expose('/svg/plot/editor/', force_secure=True)
class DataEditor(poobrains.auth.ProtectedForm):
#@app.expose('/svg/plot/editor/', force_secure=True)
class DataEditor_old(poobrains.auth.ProtectedForm):

#save_dataset = poobrains.form.Button(label="Save", type="submit")

@@ -834,25 +812,20 @@ class DataEditor(poobrains.auth.ProtectedForm):

editor_session = session['editor-sessions'][self.handle_string]

self.datasets = {}

for dataset_name in editor_session['datasets']:

dataset = Dataset()
dataset.name = dataset_name
dataset.plot_kind = editor_session['datasets'][dataset_name]['plot_kind'] or 'scatter'
dataset.title = editor_session['datasets'][dataset_name]['title']
dataset.description = editor_session['datasets'][dataset_name]['description']
dataset.data = pandas.DataFrame.from_dict(editor_session['datasets'][dataset_name]['data'])
self.editor_action = editor_session['action']

self.datasets[dataset_name] = dataset
self.dataset = Dataset()
self.dataset.name = self.handle_string
#self.dataset.plot_kind = editor_session['plot_kind'] or 'scatter'
self.dataset.plot_kind = 'scatter'
self.dataset.title = editor_session['title']
self.dataset.description = editor_session['description']
self.dataset.data = pandas.DataFrame.from_dict(editor_session['data'])

self.plot = Plot(datasets=list(self.datasets.values()))
#if len(self.dataset.data):
# self.plot_kind = PlotKindFieldset(self.handle_string)


if len(self.datasets):
self.plot_kind = PlotKindFieldset(self.handle_string)

if editor_session['action'] is None:
editor_session['action'] = 'load'

@@ -872,21 +845,16 @@ class DataEditor(poobrains.auth.ProtectedForm):

elif submit.startswith('load.'):
self.fields['load'].process(submit, self)
if len(self.datasets):
self.plot_kind = PlotKindFieldset(self.handle_string)
#session[self.handle_string] = self.data
if len(self.dataset):
#self.plot_kind = PlotKindFieldset(self.handle_string)
self.plot = Plot(dataset=self.dataset)

elif submit.startswith('plot_kind.'):
self.fields['plot_kind'].process(submit, self)

elif submit == 'save_dataset':
#stored_ds = self.dataset.save()
#return redirect(stored_ds.url('edit'))

for dataset in self.datasets.values():
if dataset.save():
flash('Saved dataset %s' % dataset.name)

stored_ds = self.dataset.save()
return redirect(stored_ds.url('edit'))

elif submit == 'delete_session':
del(session['editor-sessions'][self.handle_string])


+ 12
- 16
poobrains/themes/default/form/dataeditor.jinja Zobrazit soubor

@@ -1,23 +1,19 @@
{% extends 'form/form.jinja' %}

{% block fields %}
{% if content.datasets|length %}
{% if 'plot_kind' in content.fields %}
{{ content.fields['plot_kind'].render() }}
{% endif %}
<div class="dataeditor-plot">
{# content.dataset.plot() #}
FNORD
{{ content.plot.render('raw') }}
FNORD
</div>
{% for dataset in content.datasets.values() %}
<details>
<summary>{{ dataset.title }}</summary>
<code>{{ dataset.data }}</code>
</details>
{% endfor %}
<span>Current action: {{ content.editor_action }}</span>

{% if 'plot_kind' in content.fields %}
{{ content.fields['plot_kind'].render() }}
{% endif %}
<div class="dataeditor-plot">
{# content.dataset.plot() #}
FNORD
{% if content.plot %}
{{ content.plot.render('raw') }}
{% endif %}
FNORD
</div>

{{ content.render_fields() }}
{% endblock %}

+ 32
- 42
poobrains/themes/default/svg/plot-raw.jinja Zobrazit soubor

@@ -63,61 +63,51 @@
</g>
{% endif %}

<g class="datasets">
{% block datasets scoped %}
{% for idx in range(0, content.datasets|length) %}
<g id="{{ content.dataset.ref_id }}" class="dataset">
{% block dataset scoped %}

{% set dataset = content.datasets[idx] %}
{% if content.is_dataframe(content.dataset.data) %}
{% for series_index in content.dataset.data %}
{% set series = content.dataset.data[series_index] %}

<g id="{{ dataset.ref_id }}" class="dataset">
{% for x, y in series.iteritems() %}

{% block dataset scoped %}
<a href="#{{ content.dataset.datapoint_id(x) }}">
<g id="{{ content.dataset.datapoint_id(x) }}" class="datapoint">
{% block datapoint scoped %}

{% if content.is_dataframe(dataset.data) %}
{% for series_index in dataset.data %}
{% set series = dataset.data[series_index] %}
<use href="#marker" class="marker" x="{{ content.normalize_x(x) }}" y="{{ content.normalize_y(y) }}" />

{% for x, y in series.iteritems() %}
<text class="datapoint-value" x="{{ content.normalize_x(x) + 5 }}" y="{{ content.normalize_y(y) - 20 }}">{{ "%.5f"|format(y) }}</text>

<a href="#{{ dataset.datapoint_id(x) }}">
<g id="{{ dataset.datapoint_id(x) }}" class="datapoint">
{% block datapoint scoped %}
{% endblock %}
</g>
</a>

<use href="#marker" class="marker" x="{{ content.normalize_x(x) }}" y="{{ content.normalize_y(y) }}" />

<text class="datapoint-value" x="{{ content.normalize_x(x) + 5 }}" y="{{ content.normalize_y(y) - 20 }}">{{ "%.5f"|format(y) }}</text>

{% endblock %}
</g>
</a>

{% endfor %}
{% endfor %}
{% else %}
{% endfor %}
{% else %}

<text>{{ dataset.data.__class__.__name__ }}</text>
<text>{{ content.dataset.data.__class__.__name__ }}</text>

{% endif %}
{% 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>
</a>
<g class="description">
<a class="dataset-link" href="#{{ content.dataset.ref_id }}">
{# <text class="dataset-name" x="{{ content.inner_width }}" y="{{ 10 + (idx * 20) }}" text-anchor="end">{{ content.dataset.title }}</text> #}
</a>
<g class="description">

<foreignObject class="html" x="0" y="{{ content.plot_height + content.padding }}" width="100%" height="{{ content.description_height }}px"> {# either render HTML #}
<body xmlns="http://www.w3.org/1999/xhtml">
<h1>{{ dataset.title }}</h1>
{{ dataset.description.render() }}
</body>
</foreignObject>
<foreignObject class="html" x="0" y="{{ content.plot_height + content.padding }}" width="100%" height="{{ content.description_height }}px"> {# either render HTML #}
<body xmlns="http://www.w3.org/1999/xhtml">
<h1>{{ content.dataset.title }}</h1>
{{ content.dataset.description.render() }}
</body>
</foreignObject>

</g>
</g>

{% endblock dataset %}
</g> <!-- / .dataset -->
{% endfor %}
{% endblock %}
</g> <!-- /datasets -->
{% endblock dataset %}
</g> <!-- / .dataset -->
</svg>
</svg>

+ 2
- 0
setup.py Zobrazit soubor

@@ -12,9 +12,11 @@ setup(
'flask',
'peewee',
'pyOpenSSL', # to create TLS client certs
'nacl', # usable crypto for things like encrypted server-side sessions
'pyScss',
'pillow', # for image manipulation and generation (primarily to generate captchas)
'markdown',
'geojson',
'pyproj', # map projection
'pretty-bad-protocol', # formerly 'gnupg'
'pandas'


Načítá se…
Zrušit
Uložit