402 lines
11 KiB
Python
Executable File
402 lines
11 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
import math
|
|
import random
|
|
import json
|
|
import requests
|
|
import datetime
|
|
import flask
|
|
import numpy
|
|
import poobrains
|
|
|
|
from flask import redirect
|
|
|
|
|
|
app = poobrains.app
|
|
|
|
@app.route('/')
|
|
def front():
|
|
return redirect(News.url())
|
|
|
|
|
|
class TestSubForm(poobrains.form.Fieldset):
|
|
|
|
oink = poobrains.form.fields.Text(label="OMGWTF")
|
|
foo = poobrains.form.fields.Text(label="SUBfoo")
|
|
submit = poobrains.form.Button('submit', label="SUBSUBMIT")
|
|
|
|
|
|
@app.expose('/form')
|
|
class TestForm(poobrains.form.Form):
|
|
|
|
foo = poobrains.form.fields.Text()
|
|
bar = TestSubForm()
|
|
optin = poobrains.form.fields.Checkbox(label="Opt-in", default=False, required=True)
|
|
radio = poobrains.form.fields.Radio(type=poobrains.form.types.INT, choices=[(1, 'One'), (5, 'Five'), (23, 'Twentythree'), (42, 'Fortytwo')])
|
|
multicheck = poobrains.form.fields.Checkbox(label="Check 'em", type=poobrains.form.types.STRING, choices=[('dubs', 'dubs'), ('trips', 'TRIPS'), ('quads', 'QUADS!1!!!!')], multi=True)
|
|
completeme = poobrains.form.fields.Text(label="Lookit me, I can autocomplete without JS!", choices=[('Mr. Foo', 'foo'), ('Mr. Florb', 'florb'), ('Ms. Bar', 'bar')])
|
|
ranged = poobrains.form.fields.Range()
|
|
trigger = poobrains.form.Button('submit', label='Hit me!')
|
|
fail = poobrains.form.Button('submit', label='Fail')
|
|
|
|
def validate(self, submit):
|
|
|
|
e = poobrains.errors.CompoundError()
|
|
if submit == 'fail':
|
|
e.append(poobrains.errors.ValidationError("FAIL!", self['foo']))
|
|
|
|
if len(e):
|
|
raise e
|
|
|
|
def process(self, submit):
|
|
|
|
flask.flash('TestForm.process called!')
|
|
return self
|
|
|
|
|
|
@app.expose('/news', mode='full')
|
|
class News(poobrains.commenting.Commentable):
|
|
|
|
"""
|
|
A *news article*.
|
|
Has a nice human-readable title and markdown-enabled text.
|
|
|
|
Also, here's a random list of things to test markdown in class docstrings:
|
|
|
|
* Jane Fondas Legwarmers
|
|
* Gary Buseys Glass-eye
|
|
* Peter Thiels head on a pike
|
|
"""
|
|
|
|
class Meta:
|
|
search_fields = ['title', 'name', 'text']
|
|
|
|
title = poobrains.storage.fields.CharField()
|
|
text = poobrains.md.MarkdownField()
|
|
|
|
|
|
@app.expose('/paste', mode='full', title='Copypasta')
|
|
class Paste(poobrains.tagging.Taggable):
|
|
|
|
type = poobrains.storage.fields.CharField()
|
|
text = poobrains.storage.fields.TextField()
|
|
|
|
|
|
@app.site.box('menu_main')
|
|
def menu_main():
|
|
|
|
menu = poobrains.rendering.Menu('main')
|
|
|
|
try:
|
|
News.permissions['read'].check(flask.g.user)
|
|
menu.append(News.url(), 'News')
|
|
except poobrains.auth.AccessDenied:
|
|
pass
|
|
|
|
try:
|
|
Paste.permissions['read'].check(flask.g.user)
|
|
menu.append(Paste.url(), 'Pastes')
|
|
except poobrains.auth.AccessDenied:
|
|
pass
|
|
|
|
for url, caption in poobrains.auth.Page.main_menu_entries():
|
|
menu.append(url, caption)
|
|
|
|
return menu
|
|
|
|
|
|
class NonExposed(poobrains.auth.Administerable):
|
|
|
|
text = poobrains.storage.fields.TextField()
|
|
|
|
|
|
class NonExposedB(NonExposed):
|
|
pass
|
|
|
|
class AVeryVeryLongNameToTestMenuPositioning(poobrains.auth.Administerable):
|
|
|
|
florp = poobrains.storage.fields.BooleanField()
|
|
|
|
|
|
#class MultiPK(poobrains.auth.Administerable):
|
|
#
|
|
# class Meta:
|
|
# primary_key = peewee.CompositeKey('pk_a', 'pk_b')
|
|
#
|
|
# pk_a = poobrains.storage.fields.IntegerField()
|
|
# pk_b = poobrains.storage.fields.IntegerField()
|
|
#
|
|
#
|
|
#class NestedHandle(poobrains.auth.Administerable):
|
|
#
|
|
# class Meta:
|
|
# primary_key = peewee.CompositeKey('foreign', 'local')
|
|
#
|
|
# foreign = poobrains.storage.fields.ForeignKeyField(MultiPK)
|
|
# local = poobrains.storage.fields.CharField()
|
|
|
|
|
|
@app.site.listing(NonExposedB, '/custom', mode='full', title='Custom Listing')
|
|
def list_nonexposed(listing):
|
|
|
|
return listing
|
|
|
|
|
|
class NEO_Approaches(poobrains.analysis.EphemeralDataset):
|
|
|
|
title='NASA Near Earth Orbits'
|
|
|
|
@classmethod
|
|
def load(cls, spkid: int = 2099942): # default spkid for apophis
|
|
|
|
try:
|
|
spkid = int(spkid)
|
|
except ValueError:
|
|
raise poobrains.errors.ValidationError("Invalid SPK ID; must be an integer.")
|
|
|
|
response = requests.get('https://api.nasa.gov/neo/rest/v1/neo/%d?api_key=DEMO_KEY' % spkid)
|
|
|
|
if response.status_code != 200:
|
|
raise poobrains.errors.ValidationError("NASA API responded with error code %d." % response.status_code)
|
|
|
|
data = json.loads(response.text)
|
|
|
|
ds = cls()
|
|
ds.title = "Close approaches of %s" % (data['name_limited'] if 'name_limited' in data else data['name'])
|
|
ds.description: "**%s** belongs to orbit class **%s**; %s" % (data['name'], data['orbital_data']['orbit_class']['orbit_class_type'], data['orbital_data']['orbit_class']['orbit_class_description'])
|
|
|
|
ds.plot_data = {
|
|
'kind': 'LinePlot',
|
|
'layers': {
|
|
'approaches': {
|
|
'x': 'time',
|
|
'y': 'distance'
|
|
}
|
|
}
|
|
}
|
|
|
|
ds['time'] = {
|
|
'title': 'Time',
|
|
'description': 'Time of close approach',
|
|
'dtype': 'datetime64',
|
|
'color': None,
|
|
'observations': {},
|
|
}
|
|
|
|
ds['distance'] = {
|
|
'title': 'Distance (km)',
|
|
'description': 'Distance of close approach',
|
|
'dtype': 'float64',
|
|
'color': None,
|
|
'observations': {},
|
|
}
|
|
|
|
for index, observation in enumerate(data['close_approach_data']):
|
|
|
|
observation_time = numpy.datetime64(datetime.datetime.fromtimestamp(observation['epoch_date_close_approach'] / 1000))
|
|
observation_distance = numpy.float64(observation['miss_distance']['kilometers'])
|
|
|
|
ds['time']['observations'][index] = observation_time
|
|
ds['distance']['observations'][index] = observation_distance
|
|
|
|
return ds
|
|
|
|
|
|
class Stock_Weekly(poobrains.analysis.EphemeralDataset):
|
|
|
|
title = 'Stock (weekly)'
|
|
|
|
@classmethod
|
|
def load(cls, symbol='FB'):
|
|
|
|
response = requests.get('https://www.alphavantage.co/query?function=TIME_SERIES_WEEKLY&symbol=%s&apikey=%s' % (symbol, app.config['ALPHAVANTAGE_API_KEY']))
|
|
|
|
if response.status_code != 200:
|
|
raise poobrains.errors.ValidationError("AlphaVantage API responded with error code %d." % response.status_code)
|
|
|
|
data = json.loads(response.text)
|
|
|
|
dates = [x for x in data['Weekly Time Series'].keys()]
|
|
first_date = dates[-1]
|
|
last_date = dates[0]
|
|
|
|
ds = cls()
|
|
ds.title = symbol
|
|
ds.description = f"Data for stock symbol **{symbol}** from *{first_date}* to *{last_date}*."
|
|
|
|
ds.plot_data = {
|
|
'kind': 'LinePlot',
|
|
'layers': {
|
|
symbol: {
|
|
'x': 'time',
|
|
'y': 'price'
|
|
}
|
|
}
|
|
}
|
|
|
|
ds['time'] = {
|
|
'title': 'Time',
|
|
'description': f"Data for stock symbol **{symbol}** from *{first_date}* to *{last_date}*.",
|
|
'dtype': 'datetime64',
|
|
'color': None,
|
|
'observations': {}
|
|
}
|
|
|
|
ds['price'] = {
|
|
'title': 'Price ($)',
|
|
'description': f"Data for stock symbol **{symbol}** from *{first_date}* to *{last_date}*.",
|
|
'dtype': 'float64',
|
|
'color': None,
|
|
'observations': {}
|
|
}
|
|
|
|
for index, (datestring, datapoint) in enumerate(data['Weekly Time Series'].items()):
|
|
|
|
y, m, d = [int(x) for x in datestring.split('-')]
|
|
date = datetime.datetime(y, m, d)
|
|
|
|
ds['time']['observations'][index] = numpy.datetime64(date)
|
|
ds['price']['observations'][index] = numpy.float64(datapoint['4. close'])
|
|
#error_lower = y - float(datapoint['3. low'])
|
|
#error_upper = y - float(datapoint['2. high'])
|
|
|
|
return ds
|
|
|
|
|
|
class ConstrainedRandom(poobrains.analysis.EphemeralDataset):
|
|
|
|
title = 'Constrained random'
|
|
|
|
@classmethod
|
|
def load(cls, magnitude: int = 2, length: int = 16):
|
|
|
|
magnitude, length = int(magnitude), int(length)
|
|
|
|
ranges = []
|
|
for i in range(0, magnitude):
|
|
ranges.append(sorted((random.random(), random.random())))
|
|
|
|
ds = cls()
|
|
ds.title = f"Constrained random ({magnitude}, {length})"
|
|
ds.description = f"ConstrainedRandom of magnitude {magnitude} and length {length}."
|
|
|
|
ds.plot_data = {
|
|
'kind': 'LinePlot',
|
|
'layers': {
|
|
'plot': {
|
|
'x': 'x',
|
|
'y': 'y'
|
|
}
|
|
}
|
|
}
|
|
|
|
ds['x'] = {
|
|
'title': "X",
|
|
'description': "Equidistant steps for X-axis.",
|
|
'dtype': 'int64',
|
|
'color': None,
|
|
'observations': {},
|
|
}
|
|
|
|
ds['y'] = {
|
|
'title': "Y",
|
|
'description': "Random values for Y-axis.",
|
|
'dtype': 'float64',
|
|
'color': None,
|
|
'observations': {},
|
|
}
|
|
|
|
for index, x in enumerate(range(0, length)):
|
|
|
|
y = random.random()
|
|
for r in ranges:
|
|
y = r[0] + (r[1] - r[0]) * y
|
|
|
|
r = ranges[round((len(ranges) - 1) * random.random())] # select random range
|
|
y = r[0] + (r[1] - r[0]) * y
|
|
|
|
y *= math.sin(2 * math.pi * float(i) / length)
|
|
|
|
ds['x']['observations'][index] = numpy.int64(x)
|
|
ds['y']['observations'][index] = numpy.float64(y)
|
|
|
|
return ds
|
|
|
|
|
|
class Sine(poobrains.analysis.EphemeralDataset):
|
|
|
|
title = 'Sine'
|
|
|
|
@classmethod
|
|
def load(cls, length: int=10):
|
|
|
|
ds = cls()
|
|
ds.title = f"Sine ({length})"
|
|
ds.description = f"A full sine wave out of {length} points."
|
|
|
|
ds.plot_data = {
|
|
'kind': 'LinePlot',
|
|
'layers': {
|
|
'plot': {
|
|
'x': 'x',
|
|
'y': 'y'
|
|
}
|
|
}
|
|
}
|
|
|
|
inc = (2 * math.pi) / (length - 1)
|
|
|
|
ds['x'] = {
|
|
'title': "X",
|
|
'description': f"x for `sin(x)`; spaced at equidistance intervals of {inc}.",
|
|
'dtype': 'float64',
|
|
'color': None,
|
|
'observations': {}
|
|
}
|
|
|
|
ds['y'] = {
|
|
'title': "Y",
|
|
'description': "Result of `sin(x)`",
|
|
'dtype': 'float64',
|
|
'color': None,
|
|
'observations': {}
|
|
}
|
|
|
|
x = 0
|
|
for index in range(0, length):
|
|
|
|
ds['x']['observations'][index] = numpy.float64(x)
|
|
ds['y']['observations'][index] = numpy.float64(math.sin(x))
|
|
x += inc
|
|
|
|
return ds
|
|
|
|
|
|
#class RandomMap(poobrains.analysis.geo.EphemeralGeoData):
|
|
#
|
|
# @classmethod
|
|
# def load(cls):
|
|
#
|
|
# ds = cls()
|
|
#
|
|
# data = poobrains.analysis.geo.geojson.MultiPolygon()
|
|
#
|
|
# for poly_num in range(0, random.randrange(1, 8)):
|
|
#
|
|
# points = []
|
|
#
|
|
# for point_num in range(0, random.randrange(3, 7)):
|
|
# points.append((random.randrange(-180, 181),random.randrange(-75, 76)))
|
|
#
|
|
# data.coordinates.append([points])
|
|
#
|
|
# ds.data = poobrains.analysis.geo.geojson.dumps(data)
|
|
#
|
|
# return ds
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
#app.run()
|
|
poobrains.app.main()
|