Bläddra i källkod

Align our names

ZiB 5 månader sedan
förälder
incheckning
ed7bf31a3c
5 ändrade filer med 77 tillägg och 31 borttagningar
  1. 1 0
      .gitignore
  2. 6 6
      air2list.html
  3. 17 17
      app.py
  4. 8 8
      rpc.html
  5. 45 0
      test_roundtrip.py

+ 1 - 0
.gitignore

@@ -1 +1,2 @@
 rpc.sublime-workspace
+__pycache__/

+ 6 - 6
air2list.html

@@ -176,11 +176,11 @@
       v-for="svc in otherServices"
       :key="svc.key"
       class="result-row svc"
-      :class="{ live: svc.listed !== null && !svc.loading }"
+      :class="{ live: svc.list !== null && !svc.loading }"
     >
       <span class="row-label">{{ svc.name }}</span>
       <span v-if="svc.loading" class="spinner"></span>
-      <span v-else-if="svc.listed !== null" class="row-value">${{ svc.listed }}</span>
+      <span v-else-if="svc.list !== null" class="row-value">${{ svc.list }}</span>
     </div>
   </div>
 </div>
@@ -207,7 +207,7 @@ createApp({
         const list = await res.json();
         otherServices.splice(0, otherServices.length,
           ...list.filter(s => s.key !== 'airbnb').map(s => ({
-            ...s, listed: null, loading: false, error: null,
+            ...s, list: null, loading: false, error: null,
           }))
         );
       } catch {
@@ -219,7 +219,7 @@ createApp({
       if (!airbnbPrice.value || airbnbPrice.value <= 0) {
         net.value = null;
         globalError.value = null;
-        for (const svc of otherServices) { svc.listed = null; svc.error = null; }
+        for (const svc of otherServices) { svc.list = null; svc.error = null; }
         return;
       }
 
@@ -245,7 +245,7 @@ createApp({
 
       await Promise.all(otherServices.map(async svc => {
         svc.loading = true;
-        svc.listed = null;
+        svc.list = null;
         svc.error = null;
         try {
           const url = new URL(`${API_BASE}/net_to_list`);
@@ -254,7 +254,7 @@ createApp({
           const res = await fetch(url.toString());
           const json = await res.json();
           if (!res.ok || json.error) throw new Error(json.error || `HTTP ${res.status}`);
-          svc.listed = json.listed;
+          svc.list = json.list;
         } catch (err) {
           svc.error = err.message;
         } finally {

+ 17 - 17
app.py

@@ -55,7 +55,7 @@ def _request(request) -> dict:
 
 def _response(**data) -> str:
   _dict = { 'service_key' : data['service_key'] }
-  for k in [ 'listed', 'retail', 'net' ]:
+  for k in [ 'list', 'retail', 'net' ]:
     if data.get(k) is not None:
       _dict[k] = dollar_fmt(data.get(k))
   if data.get('details') is not None:
@@ -81,18 +81,18 @@ def _n2l(data: dict) -> None:
 
   comm_rate = svc.get('fees', {}).get('commission', 0)
   wrong_rate = svc.get('taxes', {}).get('wrong', SALES_TAX)
-  listed = (net / (1 - comm_rate - SALES_TAX + wrong_rate))
+  list_ = (net / (1 - comm_rate - SALES_TAX + wrong_rate))
   if 'fees' in svc and 'commission' in svc['fees']:
-    det['commission'] = listed * comm_rate
+    det['commission'] = list_ * comm_rate
   if 'taxes' in svc and 'wrong' in svc['taxes']:
-    det['wrong_tax'] = listed * wrong_rate
-    det['sales_tax'] = listed * SALES_TAX
+    det['wrong_tax'] = list_ * wrong_rate
+    det['sales_tax'] = list_ * SALES_TAX
     det['wrong_tax_loss'] = det['sales_tax'] - det['wrong_tax']
-  data['listed'] = round(listed, 2)
+  data['list'] = round(list_, 2)
 def _l2n(data: dict) -> None:
   svc = data['service']
   det = data['details']
-  net = data['listed'] if data.get('listed') else data['amount']
+  net = data['list'] if data.get('list') else data['amount']
   if 'fees' in svc and 'commission' in svc['fees']:
     det['commission'] = net * svc['fees']['commission']
   if 'taxes' in svc and 'wrong' in svc['taxes']:
@@ -107,7 +107,7 @@ def _l2n(data: dict) -> None:
 def _l2r(data: dict) -> None:
   svc = data['service']
   det = data['details']
-  retail = data['listed'] if data.get('listed') else data['amount']
+  retail = data['list'] if data.get('list') else data['amount']
   if 'fees' in svc and 'client_fee' in svc['fees']:
     det['client_fee'] = retail * svc['fees']['client_fee']
     retail += det['client_fee']
@@ -124,20 +124,20 @@ def _l2r(data: dict) -> None:
 def _r2l(data: dict) -> None:
   svc = data['service']
   det = data['details']
-  listed = data['retail'] if data.get('retail') else data['amount']
+  list_ = data['retail'] if data.get('retail') else data['amount']
   if 'taxes' in svc and 'flat_tax' in svc['taxes']:
     det['service_tax'] = svc['taxes']['flat_tax']
-    listed -= det['service_tax']
+    list_ -= det['service_tax']
   if 'taxes' in svc and 'wrong' in svc['taxes']:
-    det['wrong_tax'] = listed - (listed / (1 + svc['taxes']['wrong']))
-    listed -= det['wrong_tax']
+    det['wrong_tax'] = list_ - (list_ / (1 + svc['taxes']['wrong']))
+    list_ -= det['wrong_tax']
   else:
-    det['sales_tax'] = listed - (listed / (1 + SALES_TAX))
-    listed -= det['sales_tax']
+    det['sales_tax'] = list_ - (list_ / (1 + SALES_TAX))
+    list_ -= det['sales_tax']
   if 'fees' in svc and 'client_fee' in svc['fees']:
-    det['client_fee'] = listed - (listed / (1 + svc['fees']['client_fee']))
-    listed -= det['client_fee']
-  data['listed'] = round(listed, 2)
+    det['client_fee'] = list_ - (list_ / (1 + svc['fees']['client_fee']))
+    list_ -= det['client_fee']
+  data['list'] = round(list_, 2)
 
 # Routing
 routes = [

+ 8 - 8
rpc.html

@@ -382,15 +382,15 @@
 const { createApp, reactive, ref, watch, onMounted } = Vue;
 
 const CONVERSIONS = [
-  { endpoint: 'net_to_list',    from: 'net',    to: 'listed', color: 'green'  },
-  { endpoint: 'list_to_net',    from: 'listed', to: 'net',    color: 'purple' },
-  { endpoint: 'list_to_retail', from: 'listed', to: 'retail', color: 'blue'   },
-  { endpoint: 'retail_to_list', from: 'retail', to: 'listed', color: 'green'  },
+  { endpoint: 'net_to_list',    from: 'net',  to: 'list',   color: 'green'  },
+  { endpoint: 'list_to_net',    from: 'list', to: 'net',    color: 'purple' },
+  { endpoint: 'list_to_retail', from: 'list', to: 'retail', color: 'blue'   },
+  { endpoint: 'retail_to_list', from: 'retail', to: 'list', color: 'green'  },
   { endpoint: 'net_to_retail',  from: 'net',    to: 'retail', color: 'blue'   },
   { endpoint: 'retail_to_net',  from: 'retail', to: 'net',    color: 'purple' },
 ];
 
-const FIELD_LABELS = { listed: 'List', retail: 'Retail', net: 'Net' };
+const FIELD_LABELS = { list: 'List', retail: 'Retail', net: 'Net' };
 
 function labelFor(key) { return FIELD_LABELS[key] || key; }
 
@@ -442,7 +442,7 @@ createApp({
       s.loading = true;
       s.error = null;
       s.result = null;
-      s.values = { listed: null, retail: null, net: null };
+      s.values = { list: null, retail: null, net: null };
       s.details = null;
       try {
         const url = new URL(`${API_BASE}/${endpoint}`);
@@ -453,7 +453,7 @@ createApp({
         if (!res.ok || json.error) throw new Error(json.error || `HTTP ${res.status}`);
         s.result = json[conv.to] ?? null;
         s.values = {
-          listed: json.listed ?? null,
+          list: json.list ?? null,
           retail: json.retail ?? null,
           net: json.net ?? null,
         };
@@ -482,7 +482,7 @@ createApp({
     function extrasFor(conv, s) {
       if (!s.values) return [];
       const extras = [];
-      for (const k of ['listed', 'retail', 'net']) {
+      for (const k of ['list', 'retail', 'net']) {
         if (k === conv.to) continue;
         if (s.values[k] != null) extras.push({ key: k, label: labelFor(k), value: s.values[k] });
       }

+ 45 - 0
test_roundtrip.py

@@ -0,0 +1,45 @@
+import random
+import warnings
+import pytest
+from app import app, SERVICES, URL_PREFIX
+
+TOLERANCE = 0.02
+
+
+@pytest.fixture
+def client():
+    app.config['TESTING'] = True
+    with app.test_client() as c:
+        yield c
+
+
+def _call(client, endpoint, service, amount):
+    resp = client.post(f'{URL_PREFIX}/{endpoint}', json={'service': service, 'amount': amount})
+    assert resp.status_code == 200, resp.get_json()
+    return resp.get_json()
+
+
+def _near(a, b):
+    return abs(float(a) - float(b)) <= TOLERANCE
+
+
+def _check(label, a, b):
+    diff = abs(float(a) - float(b))
+    if diff > TOLERANCE:
+        warnings.warn(f"{label}: {float(a):.4f} vs {float(b):.4f} (diff={diff:.4f})", stacklevel=2)
+
+
+def test_net_retail_roundtrip(client):
+    net = random.uniform(58, 258)
+
+    for service_key in SERVICES:
+        r1 = _call(client, 'net_to_retail', service_key, net)
+        retail = float(r1['retail'])
+        r2 = _call(client, 'retail_to_net', service_key, retail)
+
+        _check(f'[{service_key}] list',   r1['list'], r2['list'])
+        _check(f'[{service_key}] net',    net,        r2['net'])
+
+        shared_keys = set(r1.get('details', {})) & set(r2.get('details', {}))
+        for k in shared_keys:
+            _check(f'[{service_key}] details.{k}', r1['details'][k], r2['details'][k])