Team Ai
Apppublic

operations-granite/ksa-development

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes
App README

KSA New Developments

A dashboard of new real estate developments across Saudi Arabia. One table, one Refresh button. Every row links the source it came from and shows when it was last verified.

Built by Granite.


Required Space secrets

The app will not start a session without these. Set them in Settings → Variables and secrets:

SecretPurpose
ANTHROPIC_API_KEYThe only paid service. Set a spend limit on the key.
DASHBOARD_PASSWORDPassword for the dashboard. There is one user.

Optional: MAX_SEARCHES_PER_SLICE (default 3), SLICE_TIMEOUT_MS (default 120000), SEARCH_MODEL (default claude-sonnet-5).

⚠️ Persistent storage

The database lives at /data/developments.db.

Without the persistent-storage add-on, that directory is wiped on every Space restart, rebuild or sleep — every refresh's data is lost and the table comes back empty. Enable persistent storage in Settings → Persistent storage if this Space is meant to hold data between sessions.

There is also no scheduler on Spaces, so the nightly cron described in the project README does not apply here. Refresh is manual — press the button.


How it works

Refresh  →  1. ~15 small searches run at once — one per watchlist source,
               plus three open-web searches
            2. Sonnet 5 turns the findings into structured records
            3. Records are saved to SQLite, matched on WAFI licence or name
                                  ↓
                            the table

No crawler. The sources that matter most — NHC, Sakani, the Saudi Press Agency — are bot-protected or render client-side, so crawling fights the site for no gain. Search reaches them.

A refresh takes about 2.5 minutes and costs about $4.50 (measured). Every run records what it actually cost and shows it above the table.

The search is split on purpose. Searches inside one API call run one after another, so a single call working through 40 of them took over three minutes and looked hung. Small calls that overlap make the wall clock the slowest one rather than the sum, and a slice that times out costs its own results instead of the whole run. Tune with MAX_SEARCHES_PER_SLICE (default 3) and SLICE_TIMEOUT_MS (default 120000).

Three of the slices ignore the watchlist and search the open web — most of the yield in testing came from trade press that isn't on the list.

The row popup

Clicking any row opens the full record: a plain description of the project, the sales contact, and every field the table has to cut short.

Contact details

A contact is only stored when a page actually printed it, and the popup shows which page that was. Where no source published one, the popup says so and offers a search instead of a number — a phone number the agent assembled from a company's usual format would look right and waste the call it was used for.

Expect this field to be empty more often than not. Press coverage announces projects; it rarely carries the sales line.

The WAFI licence column

A project with a REGA off-plan sales licence is legally sellable off-plan; one without is an announcement. The agent is instructed never to infer a licence number — an empty cell means no source published one.

What it does not claim

Coverage is these sources, not everything in the Kingdom, and it is assembled from public web pages that can be incomplete or out of date. Confirm against the linked source before acting on a row.


Sources

data/sources.json is a plain watchlist — a name, an optional site, and a sentence telling the agent what to look for. Edit it directly; no code change needed.


Styled with the Polaris Studio design system.