soroush.asadi 213af9db48
CI/CD / CI · dotnet build (push) Successful in 2m37s
CI/CD / Deploy · hamkadr (push) Successful in 1m11s
AI tag/category assignment + phone extraction from web ads
AI (when enabled, now that the server proxy is up):
- AiStructured gains phone, personName, yearsExperience, isLicensed.
- The auditor appends an authoritative output-schema to the admin prompt
  so classification stays correct even with an older stored prompt — it
  now classifies kind as shift|job|talent and extracts the contact phone
  and talent details.
- Ingestion publish prefers the AI's tags (kind/role/city/facility/phone +
  talent fields) over the heuristic parser when present.
- Default prompt updated to describe the three kinds + new fields.

Phone extraction from websites (Medjobs / generic sites), where the
number sits behind a "تماس با این آگهی" reveal:
- HtmlUtil.HarvestPhones scans the full markup for tel: links, JSON-LD
  "telephone", data-*phone* attributes, and inline Iranian mobile/landline
  numbers (Persian digits folded), normalized (mobiles 09…, landlines 0…).
- Medjobs + Website sources append harvested numbers to the ad text so the
  parser/AI capture them; manual review then prefills the phone too.
- Parser phone extraction now also captures a landline as a fallback.

Note: if a site loads the number purely via XHR (not in HTML), a
per-source reveal endpoint would be a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 08:11:14 +03:30

همکادر — Hamkadr

سامانه‌ی واسط میان کادر درمان (پزشک، پرستار، ماما، تکنسین) و بیمارستان‌ها و کلینیک‌ها برای یافتن شیفت و موقعیت استخدامی. به‌جای گشتن در کانال‌های تلگرام/بله و دیوار، همه‌ی فرصت‌ها یک‌جا — بر اساس مرکز درمانی، محل و تقویم هفتگی شمسی، با پیشنهاد هوشمند متناسب با علاقه‌مندی و فعالیت هر فرد.

A two-sided healthcare-staffing marketplace (shifts and permanent hiring) connecting all clinical staff with hospitals/clinics. See PLAN.md and FEATURES.md.

Stack

  • ASP.NET Core (Razor Pages), .NET 10 — server-rendered for SEO
  • PostgreSQL + EF Core (Npgsql)
  • RTL Persian UI, Jalali (Shamsi) dates via System.Globalization.PersianCalendar
  • Pattern-engine recommendations + anonymous interest tracking (cookie-based visitor id)
  • Self-hosted Vazirmatn font (wwwroot/fonts, no CDN), teal + coral brand palette
  • Location filters: city → district/neighborhood, plus "near me" (browser geolocation → Haversine distance sort)

Run locally

# 1. Start PostgreSQL (host port 5433)
docker compose up -d

# 2. Run the web app (applies migrations + seeds Tehran sample data on startup)
dotnet run --project src/JobsMedical.Web

Then open the URL printed in the console (e.g. http://localhost:5020).

Pages

Route Page
/ خانه — hero, role/city search, پیشنهادهای ویژه شما, latest shifts
/Shifts فهرست با فیلتر (شهر، محله/منطقه، نقش، مرکز، نوع شیفت، حقوق) + «نزدیک من»
/Shifts/Details/{id} جزئیات + اعلام تمایل / ذخیره / رد (همه رویدادها ثبت می‌شوند)
/Jobs موقعیت‌های استخدامی با فیلتر (شهر، محله، نقش، نوع همکاری) + «نزدیک من»
/Jobs/Details/{id} جزئیات موقعیت استخدامی + اعلام تمایل / ذخیره / رد
/Calendar تقویم هفتگی شمسی شیفت‌ها
/Facilities فهرست مراکز درمانی
/Preferences تنظیم علاقه‌مندی‌ها (نقش، شهر، نوع شیفت، حقوق)
/Account/Login ورود/ثبت‌نام با کد یک‌بارمصرف موبایل (OTP)
/Account/Profile پروفایل: شیفت‌ها/موقعیت‌های ذخیره‌شده و اعلام تمایل‌ها
/Admin پنل مدیریت (نقش Admin): صف آگهی‌های خام
/Admin/Review/{id} بررسی خودکار (پارسر) و انتشار آگهی به‌صورت شیفت یا استخدام
/Admin/Facilities تأیید/لغو تأیید مراکز درمانی (نشان «تأیید شده»)
/Employer پنل کارفرما: مراکز من + شمار شیفت/استخدام/متقاضی
/Employer/RegisterFacility ثبت مرکز درمانی (خودسرویس → نقش FacilityAdmin)
/Employer/PostShift، /PostJob انتشار شیفت یا موقعیت استخدامی
/Employer/Listings مدیریت آگهی‌ها (بستن/بازگشایی/حذف) + لیست متقاضیان با تماس

How recommendations work (Stage 1 — pattern engine)

RecommendationService scores open shifts against (a) explicit UserPreferences and (b) recent InterestEvents (view/save/apply/dismiss), and returns the top matches each with a Persian reason ("متناسب با نقش مورد علاقه شما"، "چون به فرصت‌های پرستار علاقه نشان دادی"). Fully explainable, no AI infra, works from the first visit. The behavioral log is the fuel for the later collaborative + ML/embedding stages (see FEATURES.md Part A).

Project status

Done: multi-role domain model, Postgres + migrations, RTL shell, browse/filter (incl. role, district, near-me), weekly Jalali calendar, shift detail + interest handoff, interest tracking + pattern-engine recommendation feed + preferences, self-hosted Vazirmatn + teal/coral palette, hiring (استخدام) listings, admin queue + heuristic listing-parser (raw channel post → structured shift/job), phone-OTP auth + visitor-history linking + profile, employer side (self-serve facility registration → FacilityAdmin role, post/manage shifts & jobs, applicants list with contact), Tehran seed data.

Both sides of the marketplace now work end-to-end: an employer self-registers a facility, posts a shift, it appears publicly, a logged-in user applies, and the employer sees that applicant's phone in their dashboard.

Compensation models

Shifts support fixed (مقطوع), hourly (ساعتی), profit-share (درصدی / سهم درآمد), توافقی, or a choice between a fixed amount and a share % ("… یا … به انتخاب شما"). JalaliDate.PayLabel centralizes the display; Shift.SharePercent holds the percentage; the listing-parser detects "۵۰٪ / درصد / سهم" from raw posts; and /Shifts has a "سهم درآمد" filter.

Scrape / ingestion engine

Pluggable IListingSources (working SampleListingSource; credential-ready Telegram/Divar stubs) → IngestionService dedupes by content hash → parses → validates → enqueues as RawListing (status New / Flagged / Discarded-spam) with a confidence score. ListingValidator scores completeness (role, location, pay, phone, length) and screens spam. IngestionWorker (hosted, config-gated Ingestion:Enabled) runs it on a timer; admins can also run it on demand from /Admin. IListingParser / HeuristicListingParser does the field extraction (kind, role, shift type, employment, pay, profit-share %, city/district, phone) — no AI dependency (LLM APIs are blocked from Iran). Admin reviews the prefilled form and publishes. Swap an LlmListingParser/real sources behind the same interfaces later.

Hour-range visualization

Every shift card, recommendation card, and detail page shows a 24-hour timeline bar (_HourBar) with the shift's hours filled and colored by type; overnight shifts wrap past midnight into two segments.

Auth

Phone OTP via OtpService (in-memory codes; dev shows the code on screen — wire Kavenegar/SMS.ir for prod). Cookie auth; the configured Auth:AdminPhone gets the Admin role. On login the anonymous hk_vid visitor (and its interest history) is linked to the user account.

Next:

  • Unified recommendations across shifts and jobs (currently shift-focused)
  • Self-serve facility posting + dashboards; verification badges
  • Real SMS gateway + Neshan/Balad interactive maps
  • LLM-backed listing parser; automated channel aggregation worker
S
Description
No description provided
Readme 4.7 MiB
Languages
C# 65.2%
HTML 30.1%
CSS 3.8%
JavaScript 0.8%