blockpy-edu / blockpy Goto Github PK
View Code? Open in Web Editor NEWBlockly that's more Python than JavaScript, powered with Skulpt
License: Apache License 2.0
Blockly that's more Python than JavaScript, powered with Skulpt
License: Apache License 2.0
We currently don't write out comments and read comments. We can fix that.
We should support a mode that hides everything but the console, and simply runs the user's code. You might optionally allow the code to be visible too.
This makes it clear that the code has executed to the end.
I don't really get the logic behind this error: "One of the times that you gave a a value was incorrect."
Jonathon did excellent work on accessing CORGIS data sources, and even arbitrary data sources. We definitely need to pull in this material.
There is a tool, called Droplet (https://github.com/droplet-editor/droplet), which is used by http://pencilcode.net and https://code.org/educate/applab
It implements issue request for JS and CofeeScript, and possibly Py(https://github.com/droplet-editor/droplet/blob/master/src/languages/python.coffee) .
Explanation, how it works http://ideas.pencilcode.net/home/htmlcss/droplet-paper.pdf
Seems a more lightweight approach than blockly...
You should be able to hide key components of the interface at will, including:
Using any reserve words (like xrange, format, etc) in the text mode and converting it to Blockly works, but when generating the code again it sees the reserved word and renames it to a safe value (so xrange2, format2, etc).
From conversation with Luke:
I dug into things some more based on this new evidence. The issue is the way that BlockPy resolves upload events (using KnockoutJS). When you upload a file, it does three things in the proper order:
The unexpected behavior is that step (1) doesn't actually immediately log a File.Edit to the server, because that actually happens through KnockoutJS's two-way data bindings: BlockPy's server module has subscribed to the model of the student's code, and logs to the server when it is notified of a change. Since this is using the same subscription model that manual edits do, this is actually rate limited (because normally we want to subsume multiple keystrokes). Therefore, it seems that the File.Edit event gets created (which is the timestamp we're using) AFTER the (2) and (3) steps complete; in the case of the first row you sent me, this happens over the course of exactly 1 second.
I think that we can account for this fairly easily - the Upload event does get a correct timestamp, so when the converter encounters one, it could just seek ahead for the next relevant File.Edit and use its CodeStateID. This would mean subsequent run events would be correct. I'll incorporate this change and send you the updated data (hopefully within the next couple hours).
This raises an interesting conceptually issue with the log data. An exceptionally fast student could do something very weird: start typing as quickly as you can to write an entire program without letting off the keyboard for a full second, and then immediately press the run button (ideally before you even stop typing). If you had pinpoint timing, you might be able to beat the notification and we'd have no way of knowing what happened. Mechanically, I can probably force KnockoutJS to force File.Edit change (if present) before a Run event gets sent, so that's not a long-term issue. But here's my question: should a File.Edit represent the start of someone typing on the keyboard, the end of someone typing on the keyboard, or should there be a second event to capture both start and end (or start/end + duration)? Perhaps it's not a big deal, but perhaps it's worth raising to the ProgSnap group?
pyodide, Mozilla's "Python scientific stack, compiled to WebAssembly" provides access to a full Python stack inside the browser.
How tightly coupled is BlockPy to Skulpt?
Is it possible to separate out the front end UI from the py environment on the back?
For example, could the Skulpt dependency be replaced by a pyodide dependency?
Could something like ThebeLab or Juniper be used to provide access to a remote Jupyter managed kernel?
Does this Jupyter extension (jupyterlab-outsource) demonstrate a BlockPy reusable or co-optable way of running a Blockly UI connected to a Jupyter kernel in a Jupyter environment?
BlockPy should be able to support custom questions
I want to translate what can be translated - into Polish language. Python is coming to schools in Poland and this tool could be very interesting for teachers here in Poland. Can anybody point me, where are the files need to be changed?
Hi, I was trying to develop a course assignment using BlockPy with the parking module. However, it seems that the feedback engine cannot process the code when I import the parking module.
Here is an easy way to reproduce the error:
import parking
in the __main__
tab and run. (Or drag any component from the Data - Parking module)This will get me an tifa_error as follow:
Feedback: Algorithm Error
tifa_error
Could not process code
The parking module itself seems working. Maybe it just cannot be properly parsed.
I also found that there is no problem importing other modules, for example, the following code can be correctly parsed.
import weather
reports = weather.get_report('New York')
print(reports)
Is this an internal bug or am I doing it wrong? Any suggestion will be appreciated. Thanks.
One of these two functions isn't working. I suspect it's the reset_output function
Hi, I've been encountering some errors while I was trying to run the sample problems provided here: https://canvas.instructure.com/courses/1134562
When I run the problem 3 - 9, it always shows an error that was in give_feedback.py
, however, the errors are different for each problem.
For example, when I run the problem 3, it throws:
Feedback: Internal Error
NameError: name 'calls_function' is not defined on line 36
Feedback Engine Error
Critical Error in BlockPy's feedback generation. Please show the above message to an instructor so they can contact a developer!
The error was in give_feedback.py.
Any idea about how to fix this?
calls to get output causing error seen below
Feedback: Internal Error
Internal Error
Error in instructor feedback. Please show the following to an instructor:
AttributeError: '' object has no attribute 'output'
File "src/lib/pedal/sandbox/compatibility.py", line 48
File "_instructor.on_run.py", line 56
Not sure how that one happened!
Students should be able to browse their history (turn off code saving during this time)
What is the state of python 3 support?
I need some help.
I have install blockpy and change en.js to pl.js (for Polish language)
Now I have blocks in Polish language, but I still have sections (like Variables) in English
Anyone can help me with that issue? I have no idea, where can I change it. In html I changed <script> ... /en.js to pl.js
I have added lang=pl to html header. Still does not work.
lease, help me.
Adam
Within Group, Assignments seem to share some context, like the libraries they have loaded.
As subject. I've cloned locally, but I'm at a loss as to how to start it up.
When doing a problem group, feedback from the previous problem in the group carries over to the next problem. The feedback window doesn't clear between problems.
Not catching the Except or Finally body; similar to the problem with if/else. And we know how much fun that was to implement :)
If you have variables that are storing HTML, then it'll be printed as HTML. Need to escape it!
Spyder has a beautiful feature that lets you explore a complicated data structure by opening multiple windows. This is an extremely desirable feature for our State Explorer. The implementation should be fairly straightforward.
The latest version of Blockly now supports zooming. We definitely want to integrate this in! Should be a simple merge.
Improve the eval side for testing student code
Is it possible to parse a source code, like:
var.function(...);
var.property = ...;
var.function(...);
into one block? As I understand it, the existing mechanism allows to parse source code for blocks that are consist of one row, like:
plt.xlabel("title")
Traceback (most recent call last):
File "./xbeetestmysql4.py", line 364, in OnTimer2
drawHum1(self,self.datacount,self.dataArray[0],SATUAN_HUM_UDARA,self.updatetimedb)
IndexError: list index out of range
import wx
import os
from xbee import XBee,ZigBee
import time
import datetime
from time import strftime
import numpy as np
import matplotlib
matplotlib.use("WXAgg")
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigCanvas
from matplotlib.backends.backend_wxagg import NavigationToolbar2WxAgg as NavigationToolbar
from matplotlib.ticker import MultipleLocator, FuncFormatter
import pylab
from matplotlib import pyplot
import MySQLdb
import MySQLdb as mdb
import sys, argparse, logging
import csv
import serial
import matplotlib.pyplot as plt
ID_TIMER = 1
ID_TIMER2 = 2
ID_TIMER3 = 3
SATUAN_HUM_UDARA = '%RH'
SATUAN_HUM_TANAH = '%RH'
SATUAN_INT_CAHAYA = 'lx'
db = MySQLdb.connect("localhost", "root", "raspberry", "sensor")
cur = db.cursor()
dbreadsensor=[]
DBtime=0
currenttime=0
#serial
arduino = serial.Serial('/dev/ttyUSB0',9600)
#def readsensor():
#data = arduino.read()
#if(data=='A'):
#data = arduino.readline()
#pieces = data.split('#')
#kelembapanUdara = pieces[0]
#kelembapanTanah = pieces[1]
#intensitasCahaya = pieces[3]
#print pieces[0]
def saveDB(hum1,hum2,intCahaya):
datetimeWrite=(time.strftime("%Y-%m-%d ")+time.strftime("%H:%M:%S"))
dateupdate=datetimeWrite
sql = (""" INSERT INTO datasensor VALUES (%s,%s,%s,%s)""",(datetimeWrite,kelembapanUdara,kelembapanTanah,intensitasCahaya))
try:
cur.execute(*sql)
db.commit()
except:
print 'Error'
def drawHum1(self,x,update,satuan,updatetime):
self.arrxTEMP.append(x)
self.arryTEMP.append(update)
self.MPL1.cla()
#self.MPL1.ylim(0,15)
self.MPL1.plot(self.arrxTEMP,self.arryTEMP,'--*g')
self.MPL1.title_MPL("Hum Udara")
self.MPL1.ylabel(SATUAN_HUM_UDARA)
self.MPL1.ShowUpdate(update,'%',updatetime)
self.MPL1.grid()
self.MPL1.UpdatePlot()
def drawHum2(self,x,update,satuan,updatetime1):
self.arrxHUM.append(x)
self.arryHUM.append(update)
self.MPL2.cla()
self.MPL2.ylim(0,100)
self.MPL2.plot(self.arrxHUM,self.arryHUM,':^b')
self.MPL2.title_MPL("Hum Tanah")
self.MPL2.ylabel(SATUAN_HUM_TANAH)
self.MPL2.ShowUpdate(update,'%',updatetime1)
self.MPL2.grid()
self.MPL2.UpdatePlot()
def drawIntCahaya(self,x,update,satuan,updatetime2):
self.arrxHUM.append(x)
self.arryHUM.append(update)
self.MPL2.cla()
self.MPL2.ylim(0,100)
self.MPL2.plot(self.arrxHUM,self.arryHUM,':^b')
self.MPL2.title_MPL("Intensitas Cahaya")
self.MPL2.ylabel(SATUAN_INT_CAHAYA)
self.MPL2.ShowUpdate(update,'lx',updatetime2)
self.MPL2.grid()
self.MPL2.UpdatePlot()
class MatplotPanel(wx.Panel):
def init(self,parent):
wx.Panel.init(self,parent=parent, id=-1)
font2 = wx.SystemSettings_GetFont(wx.SYS_SYSTEM_FONT)
font2.SetPointSize(12)
font3 = wx.SystemSettings_GetFont(wx.SYS_SYSTEM_FONT)
font3.SetPointSize(25)
self.dpi = 100
self.Figure = Figure((1.0, 2.0), dpi=self.dpi)
self.axes = self.Figure.add_axes([0.1,0.1,0.8,0.8])
self.FigureCanvas = FigCanvas(self,-1,self.Figure)
self.toolbar = NavigationToolbar(self.FigureCanvas)
self.hboxmat = wx.BoxSizer(wx.HORIZONTAL)
self.StaticText = wx.StaticText(self,-1,label='Last Update')
self.StaticText.SetFont(font2)
self.updatetime = wx.StaticText(self,-1,label='[ 00:00:00 ]:')
self.updatetime.SetFont(font2)
self.update = wx.StaticText(self,-1,label=' 0.00')
self.update.SetFont(font3)
self.satuan = wx.StaticText(self,-1,label=' ')
self.satuan.SetFont(font2)
self.hboxmat.Add(self.StaticText,proportion =0, border = 2,flag=wx.TOP)
self.hboxmat.Add(self.updatetime,proportion =0, border = 5,flag=wx.TOP)
self.hboxmat.Add(self.update,proportion =0, border = 5,flag=wx.BOTTOM)
self.hboxmat.Add(self.satuan,proportion =0, border = 5,flag=wx.TOP)
self.vboxmat = wx.BoxSizer(wx.VERTICAL)
self.vboxmat.Add(self.hboxmat, proportion =0)
self.vboxmat.Add(self.FigureCanvas, proportion =1, flag=wx.LEFT | wx.TOP | wx.EXPAND)
self.vboxmat.Add(self.toolbar, proportion =0, flag=wx.EXPAND)
self.vboxmat.AddSpacer(10)
self.SetSizer(self.vboxmat)
def UpdatePlot(self):
self.FigureCanvas.draw()
def plot(self,*args,**kwargs):
self.axes.plot(*args,**kwargs)
self.UpdatePlot()
def semilogx(self,*args,**kwargs):
self.axes.semilogx(*args,**kwargs)
self.UpdatePlot()
def semilogy(self,*args,**kwargs):
self.axes.semilogy(*args,**kwargs)
self.UpdatePlot()
def loglog(self,*args,**kwargs):
self.axes.loglog(*args,**kwargs)
self.UpdatePlot()
def grid(self,flag=True):
if flag:
self.axes.grid()
else:
self.axes.grid(False)
def title_MPL(self,TitleString="Mini Weather Station"):
self.axes.set_title(TitleString)
def xlabel(self,XabelString="X"):
self.axes.set_xlabel(XabelString)
def ylabel(self,YabelString="Y"):
self.axes.set_ylabel(YabelString)
def xticker(self,major_ticker=1.0,minor_ticker=0.1):
self.axes.xaxis.set_major_locator( MultipleLocator(major_ticker) )
self.axes.xaxis.set_minor_locator( MultipleLocator(minor_ticker) )
def yticker(self,major_ticker=1.0,minor_ticker=0.1):
self.axes.yaxis.set_major_locator( MultipleLocator(major_ticker) )
self.axes.yaxis.set_minor_locator( MultipleLocator(minor_ticker) )
def legend(self,*args,**kwargs):
self.axes.legend(*args,**kwargs)
def xlim(self,x_min,x_max):
self.axes.set_xlim(x_min,x_max)
def ylim(self,y_min,y_max):
self.axes.set_ylim(y_min,y_max)
def savefig(self,*args,**kwargs):
self.Figure.savefig(*args,**kwargs)
def cla(self):
self.axes.clear()
self.Figure.set_canvas(self.FigureCanvas)
self.UpdatePlot()
def ShowUpdate(self,update,satuan,updatetime):
self.updatetime.SetLabel('[ '+updatetime+' ]:')
self.update.SetLabel(update)
self.satuan.SetLabel(satuan)
class WQMP2ET(wx.Frame):
def __init__(self, parent, title):
super(WQMP2ET,self).__init__(parent, title=title,size=(1024,900))
self.create_menu()
self.create_status_bar()
self.InitUI()
self.Centre()
#array data
self.arrxHUM=[]
self.arryHUM=[]
self.arrxTEMP=[]
self.arryTEMP=[]
self.datacount=0
self.x=0
self.dataArray=[]
self.tandaDO='A'
self.tandabuff='A'
self.updatetimedb = time.strftime("%H:%M:%S")
#timer1 for time and date
self.timer1 = wx.Timer(self, ID_TIMER)
self.Bind(wx.EVT_TIMER, self.OnTimer1, id=ID_TIMER)
self.timer1.Start(1000) #1000ms
t = time.localtime(time.time())
st = time.strftime("%H:%M:%S", t)
#update log
self.update_log('['+st+'] System Up')
#system up
self.Show()
#timer2 for update DB
self.timer2 = wx.Timer(self, ID_TIMER2)
self.Bind(wx.EVT_TIMER, self.OnTimer2, id=ID_TIMER2)
self.timer2.Start(5*1000)
#self.timer2.Start(int(self.inintervaldb.GetValue())*60*1000) #60*1000ms
t2 = time.localtime(time.time())
st2 = time.strftime("%H:%M:%S", t2)
#system up
self.Show()
def create_menu(self):
self.menubar = wx.MenuBar()
menu_file = wx.Menu()
m_exit = menu_file.Append(-1, "E&xit\tCtrl-X", "Exit")
self.Bind(wx.EVT_MENU, self.on_exit, m_exit)
menu_help = wx.Menu()
m_about = menu_help.Append(-1, "&About\tF1", "About the demo")
self.Bind(wx.EVT_MENU, self.on_about, m_about)
self.menubar.Append(menu_file, "&File")
self.menubar.Append(menu_help, "&Help")
self.SetMenuBar(self.menubar)
def create_status_bar(self):
self.statusbar = self.CreateStatusBar(4)
self.SetStatusWidths([-1, -1, -1, 175])
def InitUI(self):
font = wx.SystemSettings_GetFont(wx.SYS_SYSTEM_FONT)
font.SetPointSize(12)
self.mainvbox = wx.BoxSizer(wx.HORIZONTAL)
self.BoxSizer=wx.BoxSizer(wx.VERTICAL)
self.BoxSizer2=wx.BoxSizer(wx.VERTICAL)
self.MPL1 = MatplotPanel(self)
self.MPL1.title_MPL("Kelembapan Udara")
self.MPL1.ylabel(SATUAN_HUM_UDARA)
self.BoxSizer.Add(self.MPL1,proportion =-1, border = 2,flag = wx.ALL | wx.EXPAND)
self.MPL2 = MatplotPanel(self)
self.MPL2.title_MPL("Kelembapan Tanah")
self.MPL2.ylabel(SATUAN_HUM_TANAH)
self.BoxSizer.Add(self.MPL2,proportion =-1, border = 2,flag = wx.ALL | wx.EXPAND)
self.mainvbox.Add(self.BoxSizer,proportion =-1, border = 2,flag = wx.ALL | wx.EXPAND)
#add test button
self.BoxSizer3=wx.BoxSizer(wx.VERTICAL)
self.RightPanel = wx.Panel(self,-1)
self.BoxSizer3.Add(self.RightPanel,proportion =0, border = 5,flag = wx.ALL | wx.EXPAND)
self.loglabel = wx.StaticText(self, label='LOG')
self.loglabel.SetFont(font)
self.logtext = wx.TextCtrl(self,size=(100,250),style=wx.TE_MULTILINE,)
self.BoxSizer3.Add(self.loglabel,proportion =0, border = 5,flag = wx.ALL | wx.EXPAND)
self.BoxSizer3.Add(self.logtext,proportion =0, border = 5,flag = wx.ALL | wx.EXPAND)
self.mainvbox.Add(self.BoxSizer3,proportion =0, border = 2,flag = wx.ALL | wx.EXPAND)
self.SetSizer(self.mainvbox)
self.FlexGridSizer=wx.FlexGridSizer( rows=8, cols=2, vgap=5,hgap=5)
self.FlexGridSizer.SetFlexibleDirection(wx.BOTH)
textsetting = wx.StaticText(self.RightPanel, label='Settings')
textsetting.SetFont(font)
textsetting2 = wx.StaticText(self.RightPanel, label='')
textsetting2.SetFont(font)
self.FlexGridSizer.Add(textsetting,proportion =0, border = 2,flag = wx.ALL | wx.EXPAND)
self.FlexGridSizer.Add(textsetting2,proportion =0, border = 2,flag = wx.ALL | wx.EXPAND)
#datepicker
textsettingd = wx.StaticText(self.RightPanel, label='Save DB to CSV File')
textsettingd.SetFont(font)
textsetting2d = wx.StaticText(self.RightPanel, label='')
textsetting2d.SetFont(font)
self.buttonsave = wx.Button(self.RightPanel,-1,"Save")
self.buttonsave.Bind(wx.EVT_BUTTON,self.ButtonsaveEvent)
self.datepicker = wx.DatePickerCtrl(parent=self.RightPanel,id=wx.ID_ANY)
self.FlexGridSizer.Add(textsettingd,proportion =0, border = 2,flag = wx.ALL | wx.EXPAND)
self.FlexGridSizer.Add(textsetting2d,proportion =0, border = 2,flag = wx.ALL | wx.EXPAND)
self.FlexGridSizer.Add(self.datepicker,proportion =0, border = 2,flag = wx.ALL | wx.EXPAND)
self.FlexGridSizer.Add(self.buttonsave,proportion =0, border = 2,flag = wx.ALL | wx.EXPAND)
self.RightPanel.SetSizer(self.FlexGridSizer)
self.Centre(wx.BOTH)
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
def OnTimer1(self, event):
datetimeWrite=(time.strftime("%d-%b-%y ")+time.strftime("%H:%M:%S"))
self.statusbar.SetStatusText(datetimeWrite, 3)
self.statusbar.Refresh()
if (time.strftime("%H:%M:%S")<'00:15:10' and self.tandabuff=='B'):
self.tandabuff='A'
if (time.strftime("%H:%M:%S")>'00:15:10' and self.tandabuff=='A'):
self.arrxHUM=[]
self.arryHUM=[]
self.arrxTEMP=[]
self.arryTEMP=[]
self.tandabuff='B'
self.update_log('['+time.strftime("%H:%M:%S")+'] Clear Buffer!!')
def OnTimer2(self, event):
#st2 = time.strftime('%H:%M:%S')
#self.x = self.x+1
#dataArray = data.split('#')
#drawHum1(self,dataArray[0],st2)
#drawHum2(self,dataArray[1],st2)
self.updatetimedb=time.strftime("%H:%M:%S")
data_sensor = arduino.read()
if(data_sensor=='A'):
data_sensor = arduino.readline()
self.dataArray = data_sensor.split('#')
self.datacount=self.datacount+1
**drawHum1(self,self.datacount,self.dataArray[0],SATUAN_HUM_UDARA,self.updatetimedb)**
def ButtonsaveEvent(self,event):
self.datevalue = self.datepicker.GetValue().Format('%Y-%m-%d')
file_choices = "CSV (*.csv)|*.csv"
dlg = wx.FileDialog(
self,
message="Save data as...",
defaultDir=os.getcwd(),
defaultFile=self.datevalue+".csv",
wildcard=file_choices,
style=wx.SAVE)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
filename=open(path,'wb')
c=csv.writer(filename)
c.writerow(['Tanggal-Jam','Temperature(degree C)','Humidity(%RH)'])
#get data from database
sql = ("""SELECT * FROM data sensor WHERE DATE(tanggal)=DATE(%s)""",(self.datevalue))
try:
cur.execute(*sql)
rows = cur.fetchall()
for row in rows:
c.writerow([row[0],row[1],row[2]])
self.update_log('['+time.strftime("%H:%M:%S")+'] CSV Success !!')
except:
self.update_log('['+time.strftime("%H:%M:%S")+'] CSV Error !!')
dlg.Destroy()
#pass
def update_log(self,datatext):
self.logtext.AppendText(datatext + "\n")
def on_exit(self, event):
self.Close(True)
def OnCloseWindow(self, event):
db.close()
cur.close()
self.Destroy()
def on_warning(self, event):
msg = """
Mini Weather Station
"""
dlg = wx.MessageDialog(self, msg, "Waring", wx.OK)
dlg.ShowModal()
dlg.Destroy()
def on_about(self, event):
msg = """
Mini Weather Station
RPi2 and Whatsapp
"""
dlg = wx.MessageDialog(self, msg, "About", wx.OK)
dlg.ShowModal()
dlg.Destroy()
if name == 'main':
app = wx.App(False)
WQMP2ET(None, title='Data Sensor')
app.MainLoop()
Make sure that they have the expected keys, help students debug common dictionary issues.
It should be easy. Mostly support different sizes for Skinny/Wide modes.
For errors, code coverage (+code coverage button), and the other thing.
When I define a function in Functions with a return block and call it, the calling block has no output handle. How can I print the returned value?
(I use a cloned version running on a PC.)
Generators are a weird beast:
https://greentreesnakes.readthedocs.io/en/latest/nodes.html#comprehensions
But I've heard they freak out the abstract interpreter, since it doesn't process them as expected. We'll need to look into this.
There are a number of Python features that we don't have Block support for yet:
Blockly just has a nebulous "Number" type, and we can do better than that. We'll just create a new block that acts like a number only makes sure its a float.
I tried to make a start on a Dockerfile for blockpy so it can be run as a container, but whilst everything seems to compile and the server appears to start, I can't actually see Blockpy in a browser, so I'm presumably doing s/thing wrong s/where.
FWIW, here's where I'm at to date:
FROM python:2
MAINTAINER [email protected]
RUN apt-get update && apt-get install -y unzip wget openjdk-7-jre-headless
RUN apt-get clean
RUN wget https://github.com/RealTimeWeb/blockpy/archive/master.zip -O blockpy-master.zip
RUN unzip blockpy-master.zip && rm blockpy-master.zip
RUN wget https://github.com/google/closure-library/archive/master.zip -O closure-library-master.zip
RUN unzip closure-library-master.zip
RUN rm -r blockpy-master/closure-library
RUN mv /closure-library-master/ /blockpy-master/closure-library/
WORKDIR /blockpy-master/blockly
RUN pip install GitPython
RUN python build.py
WORKDIR /blockpy-master/skulpt
RUN python skulpt.py dist
WORKDIR /blockpy-master/server
RUN pip install flask Flask-SQLAlchemy flask-admin flask-security flask-script sqlalchemy pylti pyOpenSSL
RUN pip install Flask-Assets
RUN pip install lxml pygments
RUN mkdir -p /blockpy-master/server/log/student_interactions/
RUN python manage.py reset_db
#Need nginx to start a server and run with the blockpy.html file as index.html?
EXPOSE 5000
CMD ["python", "manage.py","runserver"]
We can improve NameError to be aware of the commonly imported datasets.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.