
Everything your clinic's IT team needs to connect your practice management system to the ORVICS kinetic engine — ready-to-deploy bridge code included.
Your PMS
Dentrix, Open Dental, Eaglesoft, etc.
ORVICS Bridge
Lightweight script (below) runs on your server
ORVICS Engine
Cloud — kinetic analysis & directives
Dashboard
Real-time alerts & flow timeline
Open Dental
MySQL · REST API available
Dentrix
SQL Server · Dentrix API (limited)
Eaglesoft
SQL Server · Database access
Curve Dental
Cloud · REST API
Denticon
Cloud · REST API
Practice Web
SQL Server · Database access
Don't see your system? ORVICS works with any PMS that has database or API access. Contact your IT team.
pip install requests pymysql pyodbc scheduleCONFIG section at the top of the script. Enter your database credentials, your ORVICS API key, and the endpoint URL. The script includes an Open Dental example — adjust the SQL query for your specific PMS schema.get_baseline_for_procedure() function to match your practice's standard procedure durations. These baselines are what the engine measures deviation against — they power the kinetic analysis.python orvics_bridge.py. It will poll your PMS every 60 seconds and push flow readings to the ORVICS engine. Your dashboard updates in real time.#!/usr/bin/env python3
"""
ORVICS Bridge Service — Connects your dental PMS to the ORVICS kinetic engine.
This lightweight service polls your practice management database for appointment
status changes, computes elapsed flow times, and pushes readings to the ORVICS
ingest endpoint for real-time kinetic analysis.
Supports: Open Dental (MySQL), Dentrix/Eaglessoft (SQL Server), and any
PMS with database or API access.
Setup:
1. pip install requests pymysql pyodbc schedule
2. Edit CONFIG below with your PMS credentials and ORVICS API key
3. python orvics_bridge.py
"""
import requests
import schedule
import time
import json
from datetime import datetime, timedelta
# ============================================================
# CONFIGURATION — Edit these values for your clinic
# ============================================================
ORVICS_API_KEY = "YOUR_ORVICS_API_KEY_HERE" # From your ORVICS dashboard
ORVICS_ENDPOINT = "https://looqoe.com/api/functions/orvicsIngest"
# PMS Database (Open Dental example — adjust for your system)
DB_HOST = "localhost"
DB_PORT = 3306
DB_NAME = "opendental"
DB_USER = "root"
DB_PASS = "your_password"
# Polling interval (seconds). 60 = check every minute.
POLL_INTERVAL = 60
# ============================================================
# PMS DATA EXTRACTION — Adapt for your specific system
# ============================================================
def get_active_appointments():
"""
Query your PMS for appointments that are currently in progress
or recently completed. Returns a list of dicts with:
- chair_label: operatory/chair name
- patient_id: anonymized ID (NO patient names)
- flow_type: which metric this reading represents
- flow_value: elapsed minutes
- baseline: expected duration for this procedure type
"""
import pymysql
conn = pymysql.connect(
host=DB_HOST, port=DB_PORT, user=DB_USER,
password=DB_PASS, database=DB_NAME
)
cursor = conn.cursor(pymysql.cursors.DictCursor)
# Open Dental example: query appointment table for in-progress appts
cursor.execute("""
SELECT
OperatoryName AS chair_label,
PatNum AS patient_id,
AptDateTime AS start_time,
Pattern AS duration_pattern,
ProcDescript AS procedure_desc
FROM appointment
WHERE AptStatus = 2 -- Scheduled
AND AptDateTime <= NOW()
AND AptDateTime >= NOW() - INTERVAL 4 HOUR
""")
appointments = cursor.fetchall()
conn.close()
readings = []
now = datetime.now()
for appt in appointments:
start = appt["start_time"]
if isinstance(start, str):
start = datetime.fromisoformat(start)
elapsed_min = (now - start).total_seconds() / 60
# Map procedure to a baseline (customize for your codes)
baseline = get_baseline_for_procedure(appt["procedure_desc"])
readings.append({
"flow_type": "procedure_duration",
"flow_value": round(elapsed_min, 1),
"baseline": baseline,
"patient_id": f"Chair-{appt['chair_label']}-P",
"chair_label": appt["chair_label"],
})
return readings
def get_baseline_for_procedure(proc_desc):
"""Map your procedure codes to expected baseline durations (minutes)."""
baselines = {
"cleaning": 45,
"exam": 30,
"filling": 60,
"crown": 90,
"extraction": 60,
"root canal": 120,
"whitening": 75,
}
desc_lower = (proc_desc or "").lower()
for key, val in baselines.items():
if key in desc_lower:
return val
return 45 # Default baseline
# ============================================================
# ORVICS INGEST — Push readings to the kinetic engine
# ============================================================
def push_to_orvics(readings):
"""Send flow readings to the ORVICS ingest endpoint."""
if not readings:
return
headers = {
"Authorization": f"Bearer {ORVICS_API_KEY}",
"Content-Type": "application/json",
}
for reading in readings:
try:
resp = requests.post(
ORVICS_ENDPOINT,
headers=headers,
json=reading,
timeout=10
)
result = resp.json()
directive = result.get("directive_key", "unknown")
deviation = result.get("deviation_rate", 0)
if directive != "stable":
print(f" ⚠ {reading['chair_label']}: {directive} "
f"(+{deviation}% deviation)")
except Exception as e:
print(f" ✗ Failed to push reading: {e}")
# ============================================================
# MAIN LOOP
# ============================================================
def tick():
"""Poll PMS and push readings to ORVICS."""
print(f"[{datetime.now().strftime('%H:%M:%S')}] Polling PMS...")
try:
readings = get_active_appointments()
print(f" Found {len(readings)} active appointments")
push_to_orvics(readings)
except Exception as e:
print(f" Error: {e}")
if __name__ == "__main__":
print("=" * 60)
print(" ORVICS Bridge Service — Kinetic Clinic Flow Engine")
print("=" * 60)
print(f" Endpoint: {ORVICS_ENDPOINT}")
print(f" Interval: every {POLL_INTERVAL}s")
print(f" PMS: {DB_NAME} @ {DB_HOST}")
print("=" * 60)
print()
# Run immediately, then on schedule
tick()
schedule.every(POLL_INTERVAL).seconds.do(tick)
while True:
schedule.run_pending()
time.sleep(1)
Test your API key with a single reading before deploying the bridge:
# Test the connection with a single reading:
curl -X POST https://looqoe.com/api/functions/orvicsIngest \
-H "Authorization: Bearer YOUR_ORVICS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"flow_type": "procedure_duration",
"flow_value": 52,
"baseline": 45,
"patient_id": "Chair-3-P",
"chair_label": "Chair-3"
}'
# Response:
# {
# "directive_key": "observe",
# "directive": "BOTTLENECK FORMING — OBSERVE CLOSELY",
# "deviation_rate": 15.56,
# "optimization_index": 17.06,
# "flow_mass": 7,
# ...
# }For cloud-based PMS systems (Curve Dental, Denticon) or if your team prefers JavaScript:
// Node.js bridge example (for cloud-based PMS like Curve/Denticon)
const axios = require('axios');
const ORVICS_API_KEY = 'YOUR_ORVICS_API_KEY';
const ORVICS_ENDPOINT = 'https://looqoe.com/api/functions/orvicsIngest';
async function pushReading(reading) {
const res = await axios.post(ORVICS_ENDPOINT, reading, {
headers: {
'Authorization': `Bearer ${ORVICS_API_KEY}`,
'Content-Type': 'application/json',
},
});
return res.data;
}
// Example: push a procedure duration reading
pushReading({
flow_type: 'procedure_duration',
flow_value: 52,
baseline: 45,
patient_id: 'Chair-3-P',
chair_label: 'Chair-3',
}).then(result => {
console.log('Directive:', result.directive_key);
console.log('Deviation:', result.deviation_rate + '%');
});| ORVICS Field | PMS Source | Purpose |
|---|---|---|
flow_type | Procedure code / appointment status | Defines which metric (procedure_duration, chair_idle_time, patient_wait_time, turnover_time) |
flow_value | Elapsed minutes (computed by bridge) | The raw time reading — how long the flow has been running |
baseline | Standard procedure duration (your config) | The expected "normal" time — the engine measures deviation from this |
patient_id | Appointment ID / chair-patient token | Anonymized tracking — NO patient names or PHI |
chair_label | Operatory / chair name | Identifies which physical chair the reading is from |
Privacy & PHI
ORVICS does not store patient names, dates of birth, social security numbers, or any Protected Health Information (PHI). The bridge script sends only anonymized chair/patient tokens and elapsed time readings. All patient data stays in your PMS — only operational flow telemetry reaches the engine.