- Explain why a verified shop is not proof of product authenticity.
- Work through eligibility filtering and a trimmed mean.
- Trace a QR lookup from the camera to a supplier record.
- Understand why permissions belong on the server.
Actual interface captures and explanatory diagrams. English narration uses the synthetic Microsoft Zira Desktop voice.
Read the full video transcript
Start with the real problem
In Lahore's electronics markets, two similar-looking parts may have very different quality, warranties, and prices. TrustMart helps a buyer inspect those differences before deciding.
Compare like with like
First, describe the component precisely. A phone model, component type, quality grade, and warranty define the comparison group. A cheaper but different part is not automatically a better offer.
Decide which offers qualify
The price engine keeps approved offers from verified suppliers, with stock available and timestamps no older than seven days. A revoked, stale, or unavailable offer cannot influence this calculation.
Explain the calculation
With five or more eligible offers, it removes twenty percent from each end of the sorted prices, then averages the remaining values. Smaller samples use the ordinary mean and show low confidence.
Follow the shop record
A camera scan or manual Shop I D resolves through the backend to the current supplier record. Verified describes that record. It does not prove every product is authentic or guarantee compensation.
Build trust through evidence
Customers can inspect disclosures, supplier status, and report a problem. Suppliers and administrators have separate permissions. This demo uses fictional shops and sample prices, making its limits visible alongside its features.
The problem before the interface
TrustMart started with a familiar problem in Pakistan: entering an electronics market without enough information to judge a component price or a supplier's claims. In Lahore, a buyer can visit several counters, hear several prices and still struggle to understand whether those offers describe the same thing. An attractive number is not very helpful if it belongs to a different quality grade, a different compatible model or a different warranty.
The first version was university coursework built within the constraints of MIT App Inventor. Its barcode scanner could scan Shop804 and identify the fictional Ali Electronics record. That was an important working interaction, but the prototype used TinyDB and hardcoded records. The original React coursework explored the web interface separately. Neither foundation should be quietly rewritten as if it had always been a modern, deployed marketplace.
The redesign keeps that history and asks a more demanding question: what would make the information useful enough for a buyer to inspect? The result is a browser application with customer, supplier and administrator workflows, a persistent relational database, a working QR lookup path, comparable price calculations and product disclosures. Its shops and prices remain explicitly fictional. The purpose of the demo is to demonstrate the system's behaviour, not to claim a current survey of Lahore's market.
A workflow with four separate questions
The original SRS provided three useful anchors: authentication for consumers, suppliers and administrators; component price queries with a market-average calculation; and supplier validation through a Shop ID or QR code. The modern interface expands those paths, but the data flow still has to answer a small set of concrete questions.
What is the customer comparing? Which offers are eligible? How was the displayed number calculated? What does the current supplier record say? Splitting these questions is useful because an error in one should not be hidden by a polished answer to another. A shop can have a valid record while one of its offers is stale. A component can have a price while its repair history remains unknown.
- 01
Describe the component
Match component type, phone compatibility, quality grade and warranty before comparing.
- 02
Filter the offers
Keep approved, verified, in-stock offers with a valid timestamp no older than seven days.
- 03
Calculate with context
Sort eligible prices. With five or more, trim 20 percent from each tail; otherwise use the mean.
- 04
Inspect the supplier
Resolve a Shop ID or QR through the backend, then show its current record and disclosures.
The customer-facing interface is a React and TypeScript application. Flask handles the server routes, while SQLite stores the demo's relational records. This is a documented development database rather than a claim that a local file is automatically the right infrastructure for every public marketplace. The important architectural boundary is that the browser presents information and requests actions; the server decides whether an action is allowed and which records qualify.
Comparable is a rule, not a visual similarity
Consider two replacement displays for the same phone. One may be sold as Aftermarket A with one warranty period, while another has a different grade and different terms. Putting both prices into one average would make the calculation look precise while answering the wrong question. Comparability therefore starts before the statistical function: the query must describe the same component group and relevant commercial attributes.
Within that group, the price engine applies additional eligibility rules. The supplier status must be Verified; the offer must be approved; stock must be greater than zero; and the timestamp must be neither in the future nor older than seven days. A revoked supplier's low price does not become a bargain in the comparison merely because its value is numerically small.
The timestamp rule deserves attention. “Last updated” is part of the meaning of a price, not decorative text below it. A stale record can be perfectly well-formed JSON and still be unsuitable for a current comparison. Similarly, a future timestamp is rejected rather than treated as unusually fresh data. The function receives a reference time, which also makes its boundary behaviour testable.
def market_average(offers, now=None):
now = now or datetime.now(timezone.utc)
eligible = []
for o in offers:
if o['status'] != 'Verified' or not o['approved'] or o['stock'] <= 0:
continue
stamp = datetime.fromisoformat(o['updated_at'])
if stamp > now or stamp < now - timedelta(days=7):
continue
eligible.append(o)
# The DB unique constraint guarantees one listing per supplier/component/grade/warranty.
eligible.sort(key=lambda o: o['price'])
count = len(eligible)
trim = int(count * .2) if count >= 5 else 0
used = eligible[trim:count-trim] if trim else eligible
return {'average': round(sum(o['price'] for o in used)/len(used)) if used else None,
'eligible': count, 'used': len(used), 'trimmed': trim*2,
'confidence': 'Sample only' if count >= 5 else 'Low confidence',
'last_updated': max((o['updated_at'] for o in eligible), default=None),
'method': '20% trimmed mean per tail for 5+ comparable offers; otherwise untrimmed mean. Verified, approved, in-stock offers updated within 7 days only.',
'fictional': True}The source comment also records a database assumption: one current listing per supplier and comparable group. Without that restriction, one supplier could contribute several near-identical rows and gain disproportionate influence. A sensible formula does not rescue a badly defined input population.
Work through the trimmed mean
For five or more eligible prices, the implementation removes floor(count × 0.2) values from each end of the sorted list. It then calculates the mean of the remaining values. With fewer than five, it does not trim; it calculates the ordinary mean and labels the result low confidence. If no eligible offers remain, the result is absent, not zero.
Take five fictional teaching prices: PKR 8,000, 8,400, 8,600, 9,000 and 9,500. Removing one value from each tail leaves 8,400, 8,600 and 9,000. Their sum is 26,000, so the displayed integer mean is PKR 8,667. This is a reproducible example of the implemented method. It is not a recommendation about what an actual phone component should cost.
Which prices should count?
| Offer | Price | Record | Approved | Outcome |
|---|---|---|---|---|
| A | PKR 8,000 | Verified1 days old · 4 in stock | Eligible, trimmed | |
| B | PKR 8,400 | Verified2 days old · 3 in stock | Used in mean | |
| C | PKR 8,600 | Verified1 days old · 5 in stock | Used in mean | |
| D | PKR 9,000 | Verified3 days old · 2 in stock | Used in mean | |
| E | PKR 9,500 | Verified2 days old · 1 in stock | Eligible, trimmed | |
| F | PKR 4,000 | Revoked1 days old · 4 in stock | Supplier not verified | |
| G | PKR 7,200 | Verified9 days old · 3 in stock | Older than seven days | |
| H | PKR 5,000 | Verified1 days old · 0 in stock | Out of stock |
All numbers are fictional teaching inputs. This equivalent client-side calculation mirrors the backend eligibility, trimming and integer-rounding rules; it does not update live supplier records.
Try changing the most expensive eligible offer in the playground. With five eligible rows, that high tail is removed, so a dramatic increase need not change the result. Then make an offer unapproved and watch the sample shrink below the trimming threshold. The behaviour changes because the method depends on the eligible count, not because the interface has chosen a nicer-looking number.
Trimming reduces the influence of a few extreme values. It does not prove the remaining values are honest, representative or current market truth. Coordinated misleading submissions could still distort a small dataset. That is why the demo keeps its “Sample only” language, exposes counts and timestamps, and treats review workflows as part of the design rather than claiming that statistics alone establish trust.
A QR code is a lookup, not a certificate of authenticity
The modern scanner must do more than animate a camera illustration. On localhost or HTTPS, a browser can request camera permission, read a supported QR code and send the decoded reference through the lookup path. A generated store code resolves to a backend record rather than to a product claim invented by the client.
This creates several meaningful outcomes. A recognised shop can have a current status such as Verified, Warning, Revoked or Pending. A code may be unknown. Camera access may be denied. An uploaded image may not contain a readable code. Manual Shop ID entry and image-upload fallback are useful because permission, lighting and device support are real parts of the workflow.
The important security property is where the status comes from: the current server record, not the mere possession of an image containing a shop name. Copying a QR image must not manufacture a better verification state. The scanner is an input mechanism; the database and permission checks determine the response. Physical-camera acceptance on different devices remains a release check, even when the software lookup tests pass.
Transparency needs fields, evidence and unknowns
The product discussion revealed a second kind of uncertainty: a used phone and an uninstalled replacement component should not share an identical disclosure form. A buyer may want to know condition, visible defects, opening history, replaced parts, warranty terms and the basis of a seller's statement. Some fields are not applicable to one item; others are simply unknown.
“Unknown opening history” should not be rewritten as “never opened.” A manufacturer-original declaration should not be promoted into a platform authenticity guarantee. The interface therefore distinguishes seller-declared information, recorded evidence and missing history. It also gives the customer a reporting route. That route supports investigation; it does not promise insurance, automatic refunds or compensation that the platform has not implemented.
These distinctions make the interface less absolute, but more useful. A customer can inspect the record and ask a better question at handover. The supplier's phone details, opening hours and plaza directions help connect the digital record to a physical visit, while the demo avoids pretending that an ordinary map pin can navigate to an exact counter on an indoor floor.
Permissions belong behind the screen
Hiding an administrator button is helpful interface design, but it is not authorisation. The server checks sessions and roles before accepting protected changes. Supplier submissions and administrator review actions have different responsibilities. A consumer cannot become an administrator by changing a browser field or calling a route manually.
The original authentication use case also grows into a clearer account story: Google sign-in is for consumer access, while supplier and administrator workflows retain their protected login requirements. The Gemini assistant is a separate integration. A key that powers answer generation does not automatically create Google OAuth credentials, and the assistant should explain the catalogue without changing supplier verification rules.
Tests matter most at these boundaries. The backend suite exercises permissions, QR lookup, pricing, account-link behaviour and transparency rules. The useful question is not only whether the happy path renders. It is whether an invalid role, a stale offer or a conflicting account claim receives the correct controlled outcome.
What this build taught me
The strongest refinement was not the dark-purple visual treatment. It was making the source of each claim visible. A price has an eligibility set and a timestamp. A supplier has a mutable review status. A product has disclosures with different levels of evidence. A report is a request for investigation, not proof that a refund has happened.
For a student extending this project, I would begin by tracing one record across the interface, route and database. Then change one condition: revoke the supplier, remove stock, advance the clock or use an unauthorised session. Observe whether the displayed result and the permitted action change together. That exercise teaches more about trustworthy software than adding another attractive card.
The next public-release tasks are deliberately separate: persistent hosting, production account configuration, provider limits and physical-device acceptance. The current application is a working fictional-data demo. Its original repositories and prototype remain available as the foundation, while the article's code excerpts describe the modern local edition.
