Skip to content

Commit

Permalink
Merge branch 'main' into ical
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusandra committed Jun 26, 2024
2 parents a603fff + 57cab2a commit e5d869d
Show file tree
Hide file tree
Showing 222 changed files with 13,416 additions and 4,141 deletions.
5 changes: 3 additions & 2 deletions backend/app/api/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
def api_apps():
return jsonify(apps=get_app_configs())

@api.route("/apps/source/<string:keyword>", methods=["GET"])
@api.route("/apps/source", methods=["GET"])
@login_required
def api_apps_source(keyword: str):
def api_apps_source():
keyword = request.args.get("keyword")
return jsonify(get_one_app_sources(keyword))


Expand Down
8 changes: 4 additions & 4 deletions backend/app/api/tests/test_apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ def test_api_apps(self):
assert response.status_code == 200
assert 'apps' in data
assert len(data['apps']) > 0
assert 'breakIfRendering' in data['apps']
assert 'clock' in data['apps']
assert 'logic/ifElse' in data['apps']
assert 'data/clock' in data['apps']

def test_api_apps_source(self):
response = self.client.get('/api/apps/source/code')
response = self.client.get('/api/apps/source?keyword=logic/ifElse')
data = json.loads(response.data)
assert response.status_code == 200
assert 'app.nim' in data
assert 'config.json' in data
assert 'FrameConfig' in data['app.nim']
assert 'AppRoot' in data['app.nim']

def test_validate_python_frame_source_python(self):
data = {'file': 'test.py', 'source': 'print("Hello World")'}
Expand Down
679 changes: 500 additions & 179 deletions backend/app/codegen/scene_nim.py

Large diffs are not rendered by default.

63 changes: 32 additions & 31 deletions backend/app/models/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,52 @@

def get_app_configs() -> dict[str, dict]:
configs = {}
for keyword in os.listdir(local_apps_path):
local_app_path = os.path.join(local_apps_path, keyword)
if os.path.isdir(local_app_path):
config_path = os.path.join(local_app_path, "config.json")
if os.path.exists(config_path):
try:
with open(config_path, 'r') as f:
config = json.load(f)
if 'name' in config:
configs[keyword] = config
except Exception as e:
print(f"Error loading config for {keyword}: {e}")
for category in os.listdir(local_apps_path):
category_app_path = os.path.join(local_apps_path, category)
if os.path.isdir(category_app_path):
for keyword in os.listdir(category_app_path):
local_app_path = os.path.join(category_app_path, keyword)
if os.path.isdir(local_app_path):
config_path = os.path.join(local_app_path, "config.json")
if os.path.exists(config_path):
try:
with open(config_path, 'r') as f:
config = json.load(f)
if 'name' in config:
configs[category + '/' + keyword] = config
except Exception as e:
print(f"Error loading config for {category}/{keyword}: {e}")
return configs


def get_local_frame_apps() -> List[str]:
apps = os.listdir(local_apps_path)
clean_apps: List[str] = []
for keyword in apps:
local_app_path = os.path.join(local_apps_path, keyword)
app_path = os.path.join(local_app_path, "app.nim")
config_path = os.path.join(local_app_path, "config.json")
if os.path.exists(app_path) and os.path.exists(config_path):
clean_apps.append(keyword)
def get_local_frame_apps() -> list[str]:
clean_apps: list[str] = []
for category in os.listdir(local_apps_path):
category_app_path = os.path.join(local_apps_path, category)
if os.path.isdir(category_app_path):
apps = os.listdir(category_app_path)
for keyword in apps:
local_app_path = os.path.join(category_app_path, keyword)
app_path = os.path.join(local_app_path, "app.nim")
config_path = os.path.join(local_app_path, "config.json")
if os.path.exists(app_path) and os.path.exists(config_path):
clean_apps.append(category + '/' + keyword)
return clean_apps



def get_one_app_sources(keyword: str) -> Optional[dict[str, str]]:
apps = os.listdir(local_apps_path)
sources: dict[str, str] = {}
apps = get_local_frame_apps()
if keyword in apps:
local_app_path = os.path.join(local_apps_path, keyword)
app_path = os.path.join(local_app_path, "app.nim")
if os.path.exists(app_path):
with open(app_path, 'r') as f:
sources['app.nim'] = f.read()
config_path = os.path.join(local_app_path, "config.json")
if os.path.exists(config_path):
with open(config_path, 'r') as f:
sources['config.json'] = f.read()
files = os.listdir(local_app_path)
for file in files:
with open(os.path.join(local_app_path, file), 'r') as f:
sources[file] = f.read()
return sources



def get_apps_from_scenes(scenes: List[dict]) -> dict[str, dict]:
apps = {}
for scene in scenes:
Expand Down
4 changes: 2 additions & 2 deletions backend/app/models/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ def new_frame(name: str, frame_host: str, server_host: str, device: Optional[str
scenes=[],
scaling_mode="contain",
rotate=0,
background_color="#ffffff",
device=device or "web_only",
log_to_file=None, # spare the SD card from load
control_code={"enabled": "true"}
control_code={"enabled": "true"},
reboot={"enabled": "true", "crontab": "4 0 * * *"},
)
db.session.add(frame)
db.session.commit()
Expand Down
155 changes: 155 additions & 0 deletions e2e/generated/scene_dataDownloadImage.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# This file is autogenerated

{.warning[UnusedImport]: off.}
import pixie, json, times, strformat, strutils, sequtils, options

import frameos/types
import frameos/channels
import frameos/utils/image
import frameos/utils/url
import apps/render/image/app as render_imageApp
import apps/data/downloadImage/app as data_downloadImageApp
import apps/render/split/app as render_splitApp
import apps/render/color/app as render_colorApp

const DEBUG = false
let PUBLIC_STATE_FIELDS*: seq[StateField] = @[]
let PERSISTED_STATE_KEYS*: seq[string] = @[]

type Scene* = ref object of FrameScene
node1: render_imageApp.App
node2: data_downloadImageApp.App
node3: render_splitApp.App
node4: render_colorApp.App
node5: render_imageApp.App
node6: data_downloadImageApp.App

{.push hint[XDeclaredButNotUsed]: off.}
var cache0: Option[Image] = none(Image)
var cache0Time: float = 0
var cache1: Option[Image] = none(Image)
var cache1Time: float = 0

proc runNode*(self: Scene, nodeId: NodeId, context: var ExecutionContext) =
let scene = self
let frameConfig = scene.frameConfig
let state = scene.state
var nextNode = nodeId
var currentNode = nodeId
var timer = epochTime()
while nextNode != -1.NodeId:
currentNode = nextNode
timer = epochTime()
case nextNode:
of 1.NodeId: # render/image
self.node1.appConfig.image = block:
if cache0.isNone() or epochTime() > cache0Time + 900.0:
cache0 = some(block:
self.node2.get(context))
cache0Time = epochTime()
cache0.get()
self.node1.run(context)
nextNode = -1.NodeId
of 3.NodeId: # render/split
self.node3.run(context)
nextNode = -1.NodeId
of 5.NodeId: # render/image
self.node5.appConfig.image = block:
if cache1.isNone() or epochTime() > cache1Time + 900.0:
cache1 = some(block:
self.node6.get(context))
cache1Time = epochTime()
cache1.get()
self.node5.run(context)
nextNode = -1.NodeId
of 4.NodeId: # render/color
self.node4.run(context)
nextNode = 1.NodeId
else:
nextNode = -1.NodeId

if DEBUG:
self.logger.log(%*{"event": "debug:scene", "node": currentNode, "ms": (-timer + epochTime()) * 1000})

proc runEvent*(context: var ExecutionContext) =
let self = Scene(context.scene)
case context.event:
of "render":
try: self.runNode(3.NodeId, context)
except Exception as e: self.logger.log(%*{"event": "render:error", "node": 3, "error": $e.msg, "stacktrace": e.getStackTrace()})
of "setSceneState":
if context.payload.hasKey("state") and context.payload["state"].kind == JObject:
let payload = context.payload["state"]
for field in PUBLIC_STATE_FIELDS:
let key = field.name
if payload.hasKey(key) and payload[key] != self.state{key}:
self.state[key] = copy(payload[key])
if context.payload.hasKey("render"):
sendEvent("render", %*{})
else: discard

proc render*(self: FrameScene, context: var ExecutionContext): Image =
let self = Scene(self)
context.image.fill(self.backgroundColor)
runEvent(context)

return context.image

proc init*(sceneId: SceneId, frameConfig: FrameConfig, logger: Logger, persistedState: JsonNode): FrameScene =
var state = %*{}
if persistedState.kind == JObject:
for key in persistedState.keys:
state[key] = persistedState[key]
let scene = Scene(id: sceneId, frameConfig: frameConfig, state: state, logger: logger, refreshInterval: 300.0, backgroundColor: parseHtmlColor("#ffffff"))
let self = scene
result = scene
var context = ExecutionContext(scene: scene, event: "init", payload: state, hasImage: false, loopIndex: 0, loopKey: ".")
scene.execNode = (proc(nodeId: NodeId, context: var ExecutionContext) = scene.runNode(nodeId, context))
scene.node1 = render_imageApp.App(nodeName: "render/image", nodeId: 1.NodeId, scene: scene.FrameScene, frameConfig: scene.frameConfig, appConfig: render_imageApp.AppConfig(
placement: "center",
inputImage: none(Image),
offsetX: 0,
offsetY: 0,
))
scene.node2 = data_downloadImageApp.App(nodeName: "data/downloadImage", nodeId: 2.NodeId, scene: scene.FrameScene, frameConfig: scene.frameConfig, appConfig: data_downloadImageApp.AppConfig(
url: "https://frameos.net/img/logo_in_ci_tests.png",
))
scene.node3 = render_splitApp.App(nodeName: "render/split", nodeId: 3.NodeId, scene: scene.FrameScene, frameConfig: scene.frameConfig, appConfig: render_splitApp.AppConfig(
rows: 2,
inputImage: none(Image),
columns: 1,
hideEmpty: false,
render_functions: @[
@[
4.NodeId,
],
@[
5.NodeId,
],
],
render_function: 0.NodeId,
))
scene.node5 = render_imageApp.App(nodeName: "render/image", nodeId: 5.NodeId, scene: scene.FrameScene, frameConfig: scene.frameConfig, appConfig: render_imageApp.AppConfig(
inputImage: none(Image),
placement: "cover",
offsetX: 0,
offsetY: 0,
))
scene.node6 = data_downloadImageApp.App(nodeName: "data/downloadImage", nodeId: 6.NodeId, scene: scene.FrameScene, frameConfig: scene.frameConfig, appConfig: data_downloadImageApp.AppConfig(
url: "this is not an url",
))
scene.node4 = render_colorApp.App(nodeName: "render/color", nodeId: 4.NodeId, scene: scene.FrameScene, frameConfig: scene.frameConfig, appConfig: render_colorApp.AppConfig(
inputImage: none(Image),
color: parseHtmlColor("#ffffff"),
))
runEvent(context)

{.pop.}

var exportedScene* = ExportedScene(
publicStateFields: PUBLIC_STATE_FIELDS,
persistedStateKeys: PERSISTED_STATE_KEYS,
init: init,
runEvent: runEvent,
render: render
)
Loading

0 comments on commit e5d869d

Please sign in to comment.