Javis OS
Javis OS는 기업가를 돕는 에이전시 AI로, 업무를 지원하고 함께 성장합니다
하나의 슬롯 엔진으로 클라이언트·제공자·관리자 대시보드를 제공하고, 이메일은 한 번만 전송되며 비회원 예약 흐름을 지원합니다
Client, provider and admin dashboards over one slot engine, with email that is sent exactly once and a guest booking flow that needs no account.
비즈니스Python · JavaScriptGitHub @luke-zhang-cs-py온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Almanac - multi-role appointment booking. Flask + JWT, live availability from recurring hours, automatic email notifications, and coffee-chat invites that need no account.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Booking, from both directions: clients book a provider's free slots, and providers can email someone an invite that turns into a booking without the guest ever making an account.
Set weekly hours, add a block or a booking, pick a date — and the board recomputes in the tab. Each example shows the engine's own arithmetic: how many windows it read, how many slots it tiled, how many it dropped and why.
This is the slot engine only, not the platform — JWT auth, a relational
database, transactional email and a background reminder thread genuinely can't
run in a browser tab, so none of them are mimed. js/slots.js is a port of
domain/calendar_logic.py, and tools/build_static.py runs the two against
each other in a real browser over 21 scenarios before publishing, refusing to
write a single file if they disagree about one slot.
Read the full write-up → — what a free slot has to survive, how five kinds of email get sent exactly once, and every bug this has had. There's also an architecture map.
pip install -r requirements.txt
python seed_data.py # creates [email protected] / admin12345
python app.py # http://127.0.0.1:5003
No .env needed to start — SQLite and a dev JWT secret by default. It binds
127.0.0.1; set HOST=0.0.0.0 only when you mean it. Running with
FLASK_DEBUG=0 and no real SECRET_KEY is refused outright, because the dev
secret is committed and therefore public, and it signs every session token.
Switching to managed Postgres (Supabase, Neon, Render, RDS) is one environment
variable, DATABASE_URL. No code changes.
The root holds the things you run; everything you import lives in a package
named for its layer. The arrows only point one way —
core ← accounts/domain ← notify ← routes ← app.py — so an import
pointing back up is a cycle and says so immediately.
app.py Flask app factory, blueprint registration
core/ config.py, database.py settings; SQLite or Postgres
accounts/ auth.py JWT creation, RBAC decorators
domain/ calendar_logic.py the free-slot engine
coffee_chats.py, offerings.py,
pounds.py, schedule.py the rules: no HTTP, no email
notify/ mailer.py, notifications.py, transport, occasions, layout,
coffee_notifications.py, and the one background timer
email_render.py, scheduler.py
routes/ nine blueprints, one per area of the API
docs/app/ the slot engine, ported to JS and published on its own
tools/ build_static.py, build_planner.py, refresh_figures.py
app.py stays at the root because Flask(__name__) resolves templates/ and
static/ relative to its own directory. login_page.py, seed_data.py and
seed_luke.py stay because they're run as scripts — which puts their
directory on sys.path, not the project's.
python tools/build_planner.py --blank --out calendar.html
Blank, then yours. Nothing is seeded and nothing leaves the browser — the entries above were typed into the page while the frames were captured.
The planner is one self-contained HTML file you double-click — no server, no
assets, everything kept in that browser. It ships two ways. planner.html is
seeded with an invented term, which shows what it does and is the wrong place
to start a real one from: you'd be deleting somebody else's lectures first.
calendar.html is the same engine with nothing in
it — no term, no diary, no fixtures, no to-dos, no countdown.
Your own timetable is not in this repository and should not be. A term
timetable says where somebody is at every hour of every weekday; .gitignore
blocks the real seed and tests/test_planner.py fails if one appears beside
the sample. Build yours with --data and keep it outside the repo.
Where the full-stack half comes in. A file:// page can show a
notification while its tab is open and nothing at all once it's closed — it
can't register a service worker, so there's no background to run in. That's
the gap Almanac closes: export a backup from the calendar, import it, and the
server's sweep emails you before each event whether or not anything is open.
domain/schedule.py owns that, keyed on the planner's own event ids so
re-importing updates rather than duplicates. A lecture is deliberately not
an appointment — no second party, no provider to notify, no slot to hold — so
it lives in its own table with no booking semantics.
A free slot has to survive a lot. domain/calendar_logic.py turns
recurring weekly hours, one-off blocks and existing bookings into bookable
times — dropping anything that overlaps something busy at all (not just an
exact match), anything already past on today's date, and anything too short for
the requested session. A partial-unique index makes race-condition
double-booking impossible at the database level rather than in application code.
Five kinds of email, each sent exactly once. Welcome, confirmation,
cancellation, completion and a 24-hour reminder, all without anyone pressing a
button. Delivery runs on a background thread so a slow mail server never slows
a booking; every attempt is recorded in an email_log; and a unique index over
that log is what guarantees nobody is mailed the same thing twice — the
guarantee lives in the schema, not in a code path that could be missed.
Coffee chats run the other way. An email that produces a booking: send an invite, they click, they see real availability, they pick a time — with no account. A coffee chat is usually first contact, and asking an alum to register before picking a slot loses most of them. Follow-ups go out after three days, capped at two, because a third is pestering.
Money is an integer in minor units, and 0 means free — a real answer, not
a missing one.
pytest -q --cov --cov-branch --cov-fail-under=100
629 tests, 100% of 2,708 statements, and of every branch, over every module in
the repository -- the seed scripts, the standalone login page and tools/
included. CI fails a push below that. 14 of those guard the published slot-engine
demo: every copied file byte-identical to its source, no fetch() anywhere so
file:// keeps working, and every clause of the "this is not the platform"
banner still present.
The build's own check is the other half, and it's the one that can't run in CI:
it needs a real browser. (tests/test_build_static.py runs the build's logic in
CI against a stand-in browser -- the comparison, the refusals, the sabotage
list -- but only a real one can say the port agrees.) python tools/build_static.py --prove sabotages the
published port twelve ways — shifting every weekday by one, treating a
whole-day block as zero length, dropping the provider_id filter — and
requires each to be caught.
MIT — see CONTRIBUTING.md for setup and conventions.