Chaudhry Waleed commited on
Commit
b7bc437
·
1 Parent(s): 97816bd

Deploy RICS v2 (senior baseline, CPU Spaces Dockerfile)

Browse files
backend/api/routes/legacy_api_routes.py CHANGED
@@ -712,6 +712,7 @@ def _content_catalog(survey_level: int, property_type: str = "") -> dict:
712
  "group_labels": review_taxonomy.group_labels_for(resolved),
713
  "structure_mode": "content",
714
  "property_type": resolved,
 
715
  }
716
 
717
 
 
712
  "group_labels": review_taxonomy.group_labels_for(resolved),
713
  "structure_mode": "content",
714
  "property_type": resolved,
715
+ "taxonomy_version": review_taxonomy.REVIEW_TAXONOMY_VERSION,
716
  }
717
 
718
 
backend/content_based/review_taxonomy.py CHANGED
@@ -37,7 +37,7 @@ from typing import Any
37
  from backend.content_based import taxonomy
38
  from backend.domain.property_type import PROPERTY_TYPES, try_canonical_property_type
39
 
40
- REVIEW_TAXONOMY_VERSION = "r2.3"
41
 
42
  # Ordered groups, each with its fixed sub-topics (id + label only). Catch-all
43
  # "Other" chips and the second Heating chip use disambiguated on-screen labels
@@ -81,7 +81,7 @@ REVIEW_GROUPS: tuple[dict[str, Any], ...] = (
81
  },
82
  {
83
  "id": "built_in_fittings",
84
- "label": "Built-in fittings (e.g. wardrobes)",
85
  },
86
  {
87
  "id": "woodwork_joinery",
 
37
  from backend.content_based import taxonomy
38
  from backend.domain.property_type import PROPERTY_TYPES, try_canonical_property_type
39
 
40
+ REVIEW_TAXONOMY_VERSION = "r2.4"
41
 
42
  # Ordered groups, each with its fixed sub-topics (id + label only). Catch-all
43
  # "Other" chips and the second Heating chip use disambiguated on-screen labels
 
81
  },
82
  {
83
  "id": "built_in_fittings",
84
+ "label": "Built-in fittings (built-in kitchen and other fittings, not including appliances)",
85
  },
86
  {
87
  "id": "woodwork_joinery",
backend/note_quality/orchestrator.py CHANGED
@@ -21,6 +21,7 @@ import logging
21
 
22
  from backend.config import settings
23
  from backend.content_based import review_taxonomy
 
24
  from backend.note_quality import judge as judge_llm
25
  from backend.note_quality.models import (
26
  GRADE_RED,
@@ -186,7 +187,7 @@ async def grade_subtopics(items: list[GradeItem]) -> NoteQualityResult:
186
  continue
187
 
188
  notes = (item.notes or "").strip()
189
- if not notes:
190
  result.grades[code] = SubtopicGrade(
191
  code=code,
192
  grade=GRADE_RED,
 
21
 
22
  from backend.config import settings
23
  from backend.content_based import review_taxonomy
24
+ from backend.content_based.intake import EMPTY_SUBSECTION
25
  from backend.note_quality import judge as judge_llm
26
  from backend.note_quality.models import (
27
  GRADE_RED,
 
187
  continue
188
 
189
  notes = (item.notes or "").strip()
190
+ if not notes or notes == EMPTY_SUBSECTION:
191
  result.grades[code] = SubtopicGrade(
192
  code=code,
193
  grade=GRADE_RED,
backend/note_quality/rubric.py CHANGED
@@ -33,7 +33,7 @@ from backend.note_quality.rubric_entries import ENTRIES as _SOURCE
33
  # Bumped whenever the practice reissues the document, or whenever entries are
34
  # re-keyed onto a new schema as here, so cached grades can be invalidated and a
35
  # stale grade is never silently trusted.
36
- RUBRIC_VERSION = "practice-2026-08d"
37
 
38
  # Sub-topics shown to the surveyor but deliberately not graded, because the source
39
  # document does not cover them. They render neutral and stay out of the tally.
 
33
  # Bumped whenever the practice reissues the document, or whenever entries are
34
  # re-keyed onto a new schema as here, so cached grades can be invalidated and a
35
  # stale grade is never silently trusted.
36
+ RUBRIC_VERSION = "practice-2026-08e"
37
 
38
  # Sub-topics shown to the surveyor but deliberately not graded, because the source
39
  # document does not cover them. They render neutral and stay out of the tally.
backend/note_quality/rubric_entries.py CHANGED
@@ -512,25 +512,24 @@ _put(
512
  "Built-in Fittings",
513
  """**GREEN – Sufficient information**
514
 
515
- Green if the notes provide sufficient information regarding the principal built-in fittings and their general visible condition.
516
 
517
  Relevant information may include:
518
 
519
- * Type of fittings
520
- * Location
521
- * General condition
522
- * Operation where relevant
523
- * Damage/deterioration
524
- * Significant defects
525
 
526
  A complete inventory of every built-in fitting is not required.
527
 
528
  **YELLOW – Limited information**
529
 
530
- Yellow if built-in fittings are merely identified without meaningful information regarding their condition.
531
 
532
  Example Yellow:
533
- “Built-in wardrobes to bedroom.
534
 
535
  **RED – No information**
536
 
 
512
  "Built-in Fittings",
513
  """**GREEN – Sufficient information**
514
 
515
+ Green if the notes provide sufficient information regarding the principal built-in fittings (e.g., kitchen units, worktops, taps, extractor hoods) and their general visible condition, operation, or testing limitations.
516
 
517
  Relevant information may include:
518
 
519
+ * Type and description of fittings (e.g., units, worktops, recirculating/ducted extractor hoods)
520
+ * General condition and Condition Ratings (e.g., acceptable, average, good; CR1 or CR2)
521
+ * Operation where relevant (e.g., tap water flow/pressure, extractor fan functionality or need for upgrades)
522
+ * Damage/deterioration or maintenance advice (e.g., sealant condition, discolouration, water ingress risks)
523
+ * Limitations on testing (e.g., disconnected water/electricity supply or disclaimers on built-in appliances)
 
524
 
525
  A complete inventory of every built-in fitting is not required.
526
 
527
  **YELLOW – Limited information**
528
 
529
+ Yellow if built-in fittings are merely identified without meaningful information regarding their condition, operational state, or defect notes.
530
 
531
  Example Yellow:
532
+ "Kitchen contains fitted units, worktops, a mixer tap, and an extractor hood."
533
 
534
  **RED – No information**
535
 
frontend/index.html CHANGED
@@ -1332,6 +1332,8 @@ const state = {
1332
  noteStagesDumpPath: '',
1333
  /** Stage A review-chip notes keyed by review sub-topic id (for note-quality UI). */
1334
  reviewAssignments: {},
 
 
1335
  /** After content-mode Stage A, Configure shows RICS leaf cards instead of review chips. */
1336
  notesUiRics: false,
1337
  currentStep: 1,
@@ -1366,7 +1368,7 @@ const STATIC_L3_SECTIONS = [
1366
  { code:'E3', group:'E', title:'Walls and partitions', hint:'walls · partitions · damp · cracking · condition rating' },
1367
  { code:'E4', group:'E', title:'Floors', hint:'floors · suspended · solid · springy · condition rating' },
1368
  { code:'E5', group:'E', title:'Fireplaces, chimney breasts and flues', hint:'fireplaces · chimney breasts · flues · hearth · condition rating' },
1369
- { code:'E6', group:'E', title:'Built-in fittings', hint:'built-in kitchen · fitted wardrobes · cupboards · condition rating' },
1370
  { code:'E7', group:'E', title:'Woodwork', hint:'staircase · skirting · architrave · internal doors · condition rating' },
1371
  { code:'E8', group:'E', title:'Bathroom fittings', hint:'bathroom fittings · sanitaryware · shower · WC · condition rating' },
1372
  { code:'E9', group:'E', title:'Other', hint:'cellar · basement · other inside · condition rating' },
@@ -1429,7 +1431,12 @@ async function loadTemplateCatalog(surveyLevel) {
1429
  const modeQ = `&structure_mode=${encodeURIComponent(state.structureMode || 'rics')}`;
1430
  // Content mode scopes the sub-topic list by property type; RICS mode ignores it.
1431
  const ptQ = `&property_type=${encodeURIComponent(state.propertyType || 'house')}`;
1432
- const cat = await apiFetch('GET', `/templates/catalog?survey_level=${encodeURIComponent(sl)}${modeQ}${ptQ}`, null);
 
 
 
 
 
1433
  RICS_SECTIONS = (cat.sections || []).map(s => ({
1434
  code: s.code,
1435
  group: s.group,
@@ -1438,7 +1445,7 @@ async function loadTemplateCatalog(surveyLevel) {
1438
  }));
1439
  RICS_GROUP_LABELS = { ...(cat.group_labels || {}) };
1440
  state.surveyLevel = cat.survey_level;
1441
- if ((state.structureMode || 'rics') === 'content') {
1442
  state.notesUiRics = false;
1443
  REVIEW_SUBTOPICS = RICS_SECTIONS.slice();
1444
  REVIEW_GROUP_LABELS = { ...RICS_GROUP_LABELS };
@@ -1450,11 +1457,15 @@ async function loadReviewSubtopicCatalog(surveyLevel) {
1450
  const sl = (Number.isFinite(surveyLevel) && surveyLevel >= 1 && surveyLevel <= 3)
1451
  ? Math.floor(Number(surveyLevel)) : 3;
1452
  const ptQ = `&property_type=${encodeURIComponent(state.propertyType || 'house')}`;
 
 
 
1453
  const cat = await apiFetch(
1454
  'GET',
1455
- `/templates/catalog?survey_level=${encodeURIComponent(sl)}&structure_mode=content${ptQ}`,
1456
  null,
1457
  );
 
1458
  REVIEW_SUBTOPICS = (cat.sections || []).map(s => ({
1459
  code: s.code,
1460
  group: s.group,
@@ -1462,6 +1473,7 @@ async function loadReviewSubtopicCatalog(surveyLevel) {
1462
  hint: s.hint || '',
1463
  }));
1464
  REVIEW_GROUP_LABELS = { ...(cat.group_labels || {}) };
 
1465
  }
1466
 
1467
  /** Reload Configure cards as RICS leaves after content-mode Stage A remap. */
 
1332
  noteStagesDumpPath: '',
1333
  /** Stage A review-chip notes keyed by review sub-topic id (for note-quality UI). */
1334
  reviewAssignments: {},
1335
+ /** Content-mode catalog version from GET /templates/catalog (cache-bust + relabel). */
1336
+ reviewTaxonomyVersion: '',
1337
  /** After content-mode Stage A, Configure shows RICS leaf cards instead of review chips. */
1338
  notesUiRics: false,
1339
  currentStep: 1,
 
1368
  { code:'E3', group:'E', title:'Walls and partitions', hint:'walls · partitions · damp · cracking · condition rating' },
1369
  { code:'E4', group:'E', title:'Floors', hint:'floors · suspended · solid · springy · condition rating' },
1370
  { code:'E5', group:'E', title:'Fireplaces, chimney breasts and flues', hint:'fireplaces · chimney breasts · flues · hearth · condition rating' },
1371
+ { code:'E6', group:'E', title:'Built-in fittings', hint:'built-in kitchen · worktops · cupboards · extractor hood · not appliances' },
1372
  { code:'E7', group:'E', title:'Woodwork', hint:'staircase · skirting · architrave · internal doors · condition rating' },
1373
  { code:'E8', group:'E', title:'Bathroom fittings', hint:'bathroom fittings · sanitaryware · shower · WC · condition rating' },
1374
  { code:'E9', group:'E', title:'Other', hint:'cellar · basement · other inside · condition rating' },
 
1431
  const modeQ = `&structure_mode=${encodeURIComponent(state.structureMode || 'rics')}`;
1432
  // Content mode scopes the sub-topic list by property type; RICS mode ignores it.
1433
  const ptQ = `&property_type=${encodeURIComponent(state.propertyType || 'house')}`;
1434
+ const isContent = (state.structureMode || 'rics') === 'content';
1435
+ const vQ = isContent && state.reviewTaxonomyVersion
1436
+ ? `&taxonomy_version=${encodeURIComponent(state.reviewTaxonomyVersion)}`
1437
+ : '';
1438
+ const cat = await apiFetch('GET', `/templates/catalog?survey_level=${encodeURIComponent(sl)}${modeQ}${ptQ}${vQ}`, null);
1439
+ if (cat.taxonomy_version) state.reviewTaxonomyVersion = cat.taxonomy_version;
1440
  RICS_SECTIONS = (cat.sections || []).map(s => ({
1441
  code: s.code,
1442
  group: s.group,
 
1445
  }));
1446
  RICS_GROUP_LABELS = { ...(cat.group_labels || {}) };
1447
  state.surveyLevel = cat.survey_level;
1448
+ if (isContent) {
1449
  state.notesUiRics = false;
1450
  REVIEW_SUBTOPICS = RICS_SECTIONS.slice();
1451
  REVIEW_GROUP_LABELS = { ...RICS_GROUP_LABELS };
 
1457
  const sl = (Number.isFinite(surveyLevel) && surveyLevel >= 1 && surveyLevel <= 3)
1458
  ? Math.floor(Number(surveyLevel)) : 3;
1459
  const ptQ = `&property_type=${encodeURIComponent(state.propertyType || 'house')}`;
1460
+ const vQ = state.reviewTaxonomyVersion
1461
+ ? `&taxonomy_version=${encodeURIComponent(state.reviewTaxonomyVersion)}`
1462
+ : '';
1463
  const cat = await apiFetch(
1464
  'GET',
1465
+ `/templates/catalog?survey_level=${encodeURIComponent(sl)}&structure_mode=content${ptQ}${vQ}`,
1466
  null,
1467
  );
1468
+ if (cat.taxonomy_version) state.reviewTaxonomyVersion = cat.taxonomy_version;
1469
  REVIEW_SUBTOPICS = (cat.sections || []).map(s => ({
1470
  code: s.code,
1471
  group: s.group,
 
1473
  hint: s.hint || '',
1474
  }));
1475
  REVIEW_GROUP_LABELS = { ...(cat.group_labels || {}) };
1476
+ if ((state.structureMode || 'rics') === 'content') renderTopicCoverage();
1477
  }
1478
 
1479
  /** Reload Configure cards as RICS leaves after content-mode Stage A remap. */