Skip to content

Commit d8ea408

Browse files
authored
Merge pull request #53 from openbikesensor/scad-generator
Scad generator
2 parents 806819e + e3c8a98 commit d8ea408

9 files changed

Lines changed: 84724 additions & 88182 deletions

File tree

customizer/src/obs_case_customizer/app.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import datetime
23
import glob
34
import inspect
45
import json
@@ -14,16 +15,18 @@
1415
from typing import Optional
1516

1617
import pkg_resources
17-
from fastapi import FastAPI, Form, File, Request, Depends, BackgroundTasks, HTTPException
18+
from fastapi import FastAPI, Form, File, Request, Depends, HTTPException
1819
from fastapi.responses import HTMLResponse, FileResponse
1920
from fastapi.responses import RedirectResponse, JSONResponse
2021
from fastapi.templating import Jinja2Templates
2122
from fastapi.websockets import WebSocket
2223
from pydantic import BaseModel, Field, ValidationError
2324
from websockets.exceptions import ConnectionClosed
2425

25-
THREADS = int(os.environ.get('CUSTOMIZER_THREADS', 10))
26+
THREADS = int(os.environ.get('CUSTOMIZER_THREADS', 1))
2627
TIMEOUT = int(os.environ.get('CUSTOMIZER_JOB_TIMEOUT', 1200))
28+
QUEUESIZE = int(os.environ.get('CUSTOMIZER_QUEUE_LENGTH', 20))
29+
TEMPLATEDIR = pkg_resources.resource_filename(__name__, 'templates')
2730

2831
# TODO: make this configurable
2932
ALL_PARTS = [
@@ -45,20 +48,22 @@
4548
"MainCase/MainCaseLid",
4649
"MainCase/UsbCover",
4750
]
51+
4852
ALL_PARTS.extend(
4953
[f"logo/OpenBikeSensor/MainCase{l}-{inv}-{mn}"
5054
for l in ["", "Lid"]
5155
for inv in ["inverted", "normal"]
5256
for mn in ["main", "highlight"]])
5357

54-
#ALL_PARTS = ALL_PARTS[:3] # for debugging
58+
# ALL_PARTS = ALL_PARTS[:3] # for debugging
5559

5660
queue = asyncio.Queue(maxsize=20)
5761
app = FastAPI()
58-
TEMPLATEDIR = pkg_resources.resource_filename(__name__, 'templates')
5962

6063
templates = Jinja2Templates(directory=TEMPLATEDIR)
6164

65+
job_durations = [200, 200, 200]
66+
6267

6368
def field_type(entry, default_value):
6469
if entry in ["main_case_logo_svg", "main_case_lid_logo_svg"]:
@@ -82,6 +87,19 @@ def models(root):
8287
MODEL_ROOT = (ROOT / "src").resolve()
8388

8489

90+
async def queue_runner():
91+
while True:
92+
fun, arg = await queue.get()
93+
logging.info(f"running {arg}")
94+
await fun(arg)
95+
96+
97+
@app.on_event('startup')
98+
async def app_startup():
99+
asyncio.create_task(queue_runner())
100+
asyncio.create_task(queue_runner())
101+
102+
85103
def scadify(value):
86104
if isinstance(value, bool):
87105
return str(value).lower()
@@ -151,11 +169,13 @@ def package_to_zip(source: Path, target: Path):
151169
name = os.path.join(root, name)
152170
name = os.path.normpath(name)
153171
zipped_name = re.sub(r'^export/', 'OpenBikeSensor_customized/', name)
154-
print("NAME", name, zipped_name)
155172
zf.write(name, zipped_name)
156173

157174

158175
async def run_job(uid, parts=None):
176+
global job_durations
177+
starttime = datetime.datetime.now()
178+
159179
if parts == None:
160180
parts = ALL_PARTS
161181
dir_to_work = Path(tempfile.gettempdir()) / uid
@@ -178,8 +198,6 @@ async def run_job(uid, parts=None):
178198

179199
await copy_build_files_to_build_dir(dir_to_work, temp, job_config.use_custom_logo)
180200

181-
logging.error(f" run_job got {dir_to_work}")
182-
183201
try:
184202
if job_config.use_custom_logo:
185203
parts.extend([f"logo/CustomLogo/MainCase{l}-{inv}-{mn}"
@@ -210,6 +228,8 @@ async def run_job(uid, parts=None):
210228
write_info_json()
211229

212230
raise
231+
job_durations = job_durations[-2:]
232+
job_durations.append((datetime.datetime.now() - starttime).total_seconds())
213233

214234

215235
async def copy_build_files_to_build_dir(dir_to_work: Path, build_dir: Path, use_custom_logo: bool):
@@ -342,7 +362,6 @@ async def jobstate(websocket: WebSocket, uid: uuid.UUID):
342362

343363
@app.post("/job")
344364
async def form_post(request: Request,
345-
background_tasks: BackgroundTasks,
346365
main_case_logo_svg: Optional[bytes] = File(None),
347366
main_case_lid_logo_svg: Optional[bytes] = File(None),
348367
variables: CustomVariables = Depends(CustomVariables.as_form)):
@@ -361,8 +380,17 @@ async def form_post(request: Request,
361380
if len(main_case_logo_svg) == 0 and len(main_case_lid_logo_svg) == 0:
362381
variables.use_custom_logo = False
363382
variables_json_file = work_dir / "variables.json"
383+
384+
open(work_dir / "log.txt", "w").write(
385+
f"job queued, approx wait time to start {(sum(job_durations) / len(job_durations)) * max(1, queue.qsize() - 2)} seconds\n")
386+
open(work_dir / "info.json", "w").write(json.dumps({"parts": ["none"], "status": "queued", }))
387+
364388
variables_json_file.open("w").write(variables.json())
365-
background_tasks.add_task(run_job, uid)
389+
390+
try:
391+
queue.put_nowait((run_job, uid))
392+
except asyncio.QueueFull:
393+
return HTTPException(status_code=503, detail="Queue is full, please try again later")
366394

367395
if "Accept" in request.headers and request.headers["Accept"] == "application/json":
368396
return JSONResponse(RunningJob(uid=uid, **variables.dict()).dict())

customizer/src/obs_case_customizer/templates/customizer.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
{% set ft = entry|field_type(value) %}
3232

3333
{% if ft == "bool" %}
34-
<input type="checkbox"{{' checked="checked"' if value else ''}} name="{{entry}}" id="{{entry}}" />
34+
<input type="checkbox" {{'checked="checked"' if value else ''}} name="{{entry}}" id="{{entry}}" />
3535
{% elif ft == "file" %}
3636
<input type="file" name="{{entry}}" id="{{entry}}" accept="image/svg+xml"/>
3737
{% else %}

0 commit comments

Comments
 (0)