fix: brand id and genetic id are fixed
This commit is contained in:
Generated
-1
@@ -1,4 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
|
|||||||
fun useAppContext() {
|
fun useAppContext() {
|
||||||
// Context of the app under test.
|
// Context of the app under test.
|
||||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
assertEquals("com.approagency.drug", appContext.packageName)
|
assertEquals("com.approagency.pharmacy", appContext.packageName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,64 +14,78 @@ object PharmacyHtmlParser {
|
|||||||
val doc = Jsoup.parse(html)
|
val doc = Jsoup.parse(html)
|
||||||
val table = doc.select("table#TBL_patientReferral").first() ?: return emptyList()
|
val table = doc.select("table#TBL_patientReferral").first() ?: return emptyList()
|
||||||
|
|
||||||
// تشخیص ساختار بر اساس سربرگ جدول (یک بار برای کل جدول)
|
// ساختار جدول متغیر است:
|
||||||
val hasGuaranteeColumn = hasGuaranteeColumnInHeader(table)
|
// - صفحات ژنریک: [نام برند, نام داروخانه, شهرستان] (گاهی با ستون «تضمین موجودی»)
|
||||||
|
// - صفحات برند: [نام داروخانه, شهرستان] (بدون ستون برند)
|
||||||
|
// بنابراین ایندکس ستونها را از روی متن سربرگ تشخیص میدهیم نه موقعیت ثابت
|
||||||
|
val headers = table.select("thead th")
|
||||||
|
var brandIndex = -1
|
||||||
|
var pharmacyIndex = -1
|
||||||
|
var locationIndex = -1
|
||||||
|
headers.forEachIndexed { index, th ->
|
||||||
|
val text = th.text().trim()
|
||||||
|
when {
|
||||||
|
brandIndex == -1 && text.contains("برند") -> brandIndex = index
|
||||||
|
pharmacyIndex == -1 && text.contains("داروخانه") -> pharmacyIndex = index
|
||||||
|
locationIndex == -1 && (text.contains("شهرستان") || text.contains("استان")) -> locationIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val rows = table.select("tbody tr")
|
val rows = table.select("tbody tr")
|
||||||
|
|
||||||
for (row in rows) {
|
for (row in rows) {
|
||||||
val cells = row.select("td")
|
val cells = row.select("td")
|
||||||
if (cells.size < 3) continue
|
if (cells.isEmpty()) continue
|
||||||
|
|
||||||
// ایندکس ستونها بر اساس وجود ستون تضمین
|
// ستون داروخانه (ضروری) — اگر از سربرگ پیدا نشد، سلولی که لینک ph- دارد
|
||||||
val brandIndex = if (hasGuaranteeColumn) 1 else 0
|
val pIdx = if (pharmacyIndex in cells.indices) {
|
||||||
val pharmacyIndex = if (hasGuaranteeColumn) 2 else 1
|
pharmacyIndex
|
||||||
val locationIndex = if (hasGuaranteeColumn) 3 else 2
|
} else {
|
||||||
|
cells.indexOfFirst { cell ->
|
||||||
|
cell.select("a").any { it.attr("href").contains("ph-") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pIdx < 0) continue
|
||||||
|
|
||||||
// ستون برند
|
val pharmacyCell = cells[pIdx]
|
||||||
val brandCell = cells[brandIndex]
|
// پیدا کردن لینک داروخانه (لینکی که "اطلاعات تماس" نباشد)
|
||||||
val brandLink = brandCell.select("a").first()
|
val pharmacyLink = pharmacyCell.select("a").firstOrNull {
|
||||||
|
!it.text().contains("اطلاعات تماس")
|
||||||
|
}
|
||||||
|
val pharmacyName = pharmacyLink?.text()?.trim() ?: ""
|
||||||
|
val pharmacyUrl = normalizeUrl(pharmacyLink?.attr("href")?.trim() ?: "")
|
||||||
|
val pharmacyId = extractIdFromUrl(pharmacyUrl, "/ph-(\\d+)/?")
|
||||||
|
if (pharmacyName.isBlank()) continue
|
||||||
|
|
||||||
|
// ستون برند (در صفحات برند وجود ندارد)
|
||||||
|
val brandCell = brandIndex.takeIf { it in cells.indices }?.let { cells[it] }
|
||||||
|
val brandLink = brandCell?.select("a")?.first()
|
||||||
val brandName = brandLink?.text()?.trim() ?: ""
|
val brandName = brandLink?.text()?.trim() ?: ""
|
||||||
val brandUrl = normalizeUrl(brandLink?.attr("href")?.trim() ?: "")
|
val brandUrl = normalizeUrl(brandLink?.attr("href")?.trim() ?: "")
|
||||||
val brandId = extractIdFromUrl(brandUrl, "/B-(\\d+)/?")
|
val brandId = extractIdFromUrl(brandUrl, "/B-(\\d+)/?")
|
||||||
|
|
||||||
// ستون داروخانه
|
// ستون موقعیت — اگر از سربرگ پیدا نشد، سلولی که «استان» دارد
|
||||||
val pharmacyCell = cells[pharmacyIndex]
|
val locIdx = if (locationIndex in cells.indices) {
|
||||||
val pharmacyLinks = pharmacyCell.select("a")
|
locationIndex
|
||||||
|
} else {
|
||||||
// پیدا کردن لینک داروخانه (لینکی که "اطلاعات تماس" نباشد)
|
cells.indexOfFirst { it.text().contains("استان") }
|
||||||
val pharmacyLink = pharmacyLinks.firstOrNull {
|
|
||||||
!it.text().contains("اطلاعات تماس")
|
|
||||||
}
|
}
|
||||||
val pharmacyName = pharmacyLink?.text()?.trim() ?: ""
|
val locationText = locIdx.takeIf { it in cells.indices }?.let { cells[it].text().trim() } ?: ""
|
||||||
var pharmacyUrl = pharmacyLink?.attr("href")?.trim() ?: ""
|
|
||||||
|
|
||||||
// نرمال کردن URL داروخانه
|
|
||||||
pharmacyUrl = normalizeUrl(pharmacyUrl)
|
|
||||||
val pharmacyId = extractIdFromUrl(pharmacyUrl, "/ph-(\\d+)/?")
|
|
||||||
|
|
||||||
// ستون موقعیت
|
|
||||||
val locationCell = cells[locationIndex]
|
|
||||||
val locationText = locationCell.text().trim()
|
|
||||||
|
|
||||||
// استخراج استان و شهر
|
|
||||||
val province = extractProvince(locationText)
|
val province = extractProvince(locationText)
|
||||||
val city = extractCity(locationText)
|
val city = extractCity(locationText)
|
||||||
|
|
||||||
if (brandName.isNotBlank() && pharmacyName.isNotBlank()) {
|
items.add(
|
||||||
items.add(
|
PharmacyItem(
|
||||||
PharmacyItem(
|
brandName = brandName,
|
||||||
brandName = brandName,
|
brandUrl = brandUrl,
|
||||||
brandUrl = brandUrl,
|
brandId = brandId,
|
||||||
brandId = brandId,
|
pharmacyName = pharmacyName,
|
||||||
pharmacyName = pharmacyName,
|
pharmacyUrl = pharmacyUrl,
|
||||||
pharmacyUrl = pharmacyUrl,
|
pharmacyId = pharmacyId,
|
||||||
pharmacyId = pharmacyId,
|
province = province,
|
||||||
province = province,
|
city = city
|
||||||
city = city
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return items
|
return items
|
||||||
@@ -108,18 +122,6 @@ object PharmacyHtmlParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* تشخیص وجود ستون تضمین موجودی با بررسی سربرگ جدول
|
|
||||||
*/
|
|
||||||
private fun hasGuaranteeColumnInHeader(table: org.jsoup.nodes.Element): Boolean {
|
|
||||||
val headers = table.select("thead th")
|
|
||||||
if (headers.isEmpty()) return false
|
|
||||||
|
|
||||||
// بررسی میکنیم که آیا اولین ستون حاوی متن "تضمین" است
|
|
||||||
val firstHeaderText = headers.firstOrNull()?.text()?.trim() ?: ""
|
|
||||||
return firstHeaderText.contains("تضمین") || firstHeaderText.contains("موجودی")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun extractProvince(locationText: String): String {
|
private fun extractProvince(locationText: String): String {
|
||||||
// الگو: "استان تهران" یا "استان اصفهان"
|
// الگو: "استان تهران" یا "استان اصفهان"
|
||||||
val provincePattern = "استان\\s*(\\S+)".toRegex()
|
val provincePattern = "استان\\s*(\\S+)".toRegex()
|
||||||
|
|||||||
@@ -94,12 +94,13 @@ class DrugRepositoryImpl(
|
|||||||
|
|
||||||
override suspend fun searchPharmacies(
|
override suspend fun searchPharmacies(
|
||||||
genericDrugId: String,
|
genericDrugId: String,
|
||||||
|
brandIrc: String,
|
||||||
provId: String
|
provId: String
|
||||||
): List<PharmacyItem> {
|
): List<PharmacyItem> {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val html = darooyabApiService.getPharmacies(
|
val html = darooyabApiService.getPharmacies(
|
||||||
brandIrc = "0",
|
brandIrc = brandIrc,
|
||||||
genericDrugId = genericDrugId,
|
genericDrugId = genericDrugId,
|
||||||
provId = provId,
|
provId = provId,
|
||||||
cityId = "0"
|
cityId = "0"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ interface DrugRepository {
|
|||||||
suspend fun getGorohDaroei(): Result<DarmanModel>
|
suspend fun getGorohDaroei(): Result<DarmanModel>
|
||||||
suspend fun searchDrugs(params:DaroYabParams):Result<DaroYabSearchResult>
|
suspend fun searchDrugs(params:DaroYabParams):Result<DaroYabSearchResult>
|
||||||
suspend fun getDrugDetailFromYab(detailUrl: String):Result<DrugDetail>
|
suspend fun getDrugDetailFromYab(detailUrl: String):Result<DrugDetail>
|
||||||
suspend fun searchPharmacies(genericDrugId: String,provId: String): List<PharmacyItem>
|
suspend fun searchPharmacies(genericDrugId: String, brandIrc: String, provId: String): List<PharmacyItem>
|
||||||
|
|
||||||
suspend fun getPharmacyDetail(pharmacyUrl: String): PharmacyDetail
|
suspend fun getPharmacyDetail(pharmacyUrl: String): PharmacyDetail
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ class GetPharmaciesUseCase(
|
|||||||
) {
|
) {
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
genericDrugId: String,
|
genericDrugId: String,
|
||||||
|
brandIrc: String,
|
||||||
provinceId: String
|
provinceId: String
|
||||||
): List<PharmacyItem> {
|
): List<PharmacyItem> {
|
||||||
return repository.searchPharmacies(genericDrugId, provinceId)
|
return repository.searchPharmacies(genericDrugId, brandIrc, provinceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+10
-7
@@ -33,7 +33,11 @@ import com.vada.caller.ui.theme.dime
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PharmacyBottomSheet(
|
fun PharmacyBottomSheet(
|
||||||
viewModel: PharmacyViewModel, genericDrugId: String, isOpen: Boolean, onDismiss: () -> Unit
|
viewModel: PharmacyViewModel,
|
||||||
|
genericDrugId: String,
|
||||||
|
brandIrc: String,
|
||||||
|
isOpen: Boolean,
|
||||||
|
onDismiss: () -> Unit
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val sheetState = rememberModalBottomSheetState(
|
val sheetState = rememberModalBottomSheetState(
|
||||||
@@ -45,11 +49,10 @@ fun PharmacyBottomSheet(
|
|||||||
|
|
||||||
val pharmacyState by viewModel.state.collectAsState()
|
val pharmacyState by viewModel.state.collectAsState()
|
||||||
|
|
||||||
// جستجوی اولیه با استان تهران
|
// جستجوی اولیه با استان تهران — با تغییر دارو دوباره اجرا میشود
|
||||||
LaunchedEffect(Unit) {
|
// (در صورت خالی بودن شناسه، ViewModel وضعیت قبلی را پاک و خطا نمایش میدهد)
|
||||||
if (genericDrugId.isNotBlank()) {
|
LaunchedEffect(genericDrugId, brandIrc) {
|
||||||
viewModel.search(genericDrugId, "40")
|
viewModel.search(genericDrugId, brandIrc, "40")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
@@ -98,7 +101,7 @@ fun PharmacyBottomSheet(
|
|||||||
selectedProvinceId = province.id
|
selectedProvinceId = province.id
|
||||||
showProvinceDropdown = false
|
showProvinceDropdown = false
|
||||||
// جستجو با استان جدید
|
// جستجو با استان جدید
|
||||||
viewModel.search(genericDrugId, province.id)
|
viewModel.search(genericDrugId, brandIrc, province.id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,13 +128,19 @@ fun SearchScreen(
|
|||||||
Spacer(modifier = Modifier.height(MaterialTheme.dime.md))
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.md))
|
||||||
|
|
||||||
if (showPharmacyBottomSheet && selectedDrugForPharmacy != null) {
|
if (showPharmacyBottomSheet && selectedDrugForPharmacy != null) {
|
||||||
// استخراج genericDrugId از URL
|
// درخواست بر اساس نوع صفحه فرق میکند:
|
||||||
// مثال: /G-799/Fexofenadine -> 799
|
// - صفحات برند (/B-...): شناسه باید بهعنوان brandIrc ارسال شود
|
||||||
val genericId = extractGenericIdFromUrl(selectedDrugForPharmacy?.detailPageUrl ?: "")
|
// - صفحات ژنریک (/G-...): شناسه بهعنوان genericDrugId ارسال میشود
|
||||||
|
// (genericId از قبل پارس شده و در هر دو حالت همان عدد را نگه میدارد)
|
||||||
|
val drug = selectedDrugForPharmacy!!
|
||||||
|
val isBrand = drug.detailPageUrl.contains("/B-")
|
||||||
|
val genericId = if (isBrand) "0" else drug.genericId
|
||||||
|
val brandIrc = if (isBrand) drug.genericId else "0"
|
||||||
|
|
||||||
PharmacyBottomSheet(
|
PharmacyBottomSheet(
|
||||||
viewModel = pharmacyViewModel,
|
viewModel = pharmacyViewModel,
|
||||||
genericDrugId = genericId,
|
genericDrugId = genericId,
|
||||||
|
brandIrc = brandIrc,
|
||||||
isOpen = showPharmacyBottomSheet,
|
isOpen = showPharmacyBottomSheet,
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
showPharmacyBottomSheet = false
|
showPharmacyBottomSheet = false
|
||||||
@@ -236,9 +242,3 @@ fun SearchScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// تابع کمکی برای استخراج ID از URL
|
|
||||||
private fun extractGenericIdFromUrl(url: String): String {
|
|
||||||
val pattern = "/G-(\\d+)/".toRegex()
|
|
||||||
return pattern.find(url)?.groupValues?.get(1) ?: ""
|
|
||||||
}
|
|
||||||
+16
-7
@@ -38,6 +38,7 @@ class PharmacyViewModel(
|
|||||||
val pharmacyDetailStates: StateFlow<Map<String, PharmacyDetailState>> = _pharmacyDetailStates.asStateFlow()
|
val pharmacyDetailStates: StateFlow<Map<String, PharmacyDetailState>> = _pharmacyDetailStates.asStateFlow()
|
||||||
|
|
||||||
private var currentGenericDrugId: String = ""
|
private var currentGenericDrugId: String = ""
|
||||||
|
private var currentBrandIrc: String = "0"
|
||||||
private var currentProvinceId: String = "0"
|
private var currentProvinceId: String = "0"
|
||||||
|
|
||||||
fun loadPharmacyDetail(pharmacyUrl: String) {
|
fun loadPharmacyDetail(pharmacyUrl: String) {
|
||||||
@@ -67,16 +68,26 @@ class PharmacyViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun search(genericDrugId: String, provinceId: String) {
|
fun search(genericDrugId: String, brandIrc: String, provinceId: String) {
|
||||||
if (genericDrugId.isBlank()) return
|
|
||||||
|
|
||||||
currentGenericDrugId = genericDrugId
|
currentGenericDrugId = genericDrugId
|
||||||
|
currentBrandIrc = brandIrc
|
||||||
currentProvinceId = provinceId
|
currentProvinceId = provinceId
|
||||||
|
|
||||||
|
// پاک کردن نتایج جستجوی قبلی تا دادهی قبلی نمایش داده نشود
|
||||||
|
_pharmacyDetailStates.value = emptyMap()
|
||||||
|
|
||||||
|
// حداقل یکی از شناسهها (ژنریک یا برند) باید معتبر باشد
|
||||||
|
val hasGeneric = genericDrugId.isNotBlank() && genericDrugId != "0"
|
||||||
|
val hasBrand = brandIrc.isNotBlank() && brandIrc != "0"
|
||||||
|
if (!hasGeneric && !hasBrand) {
|
||||||
|
_state.value = PharmacyState.Error("شناسه دارو یافت نشد")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_state.value = PharmacyState.Loading
|
_state.value = PharmacyState.Loading
|
||||||
try {
|
try {
|
||||||
val result = getPharmaciesUseCase(genericDrugId, provinceId)
|
val result = getPharmaciesUseCase(genericDrugId, brandIrc, provinceId)
|
||||||
_state.value = PharmacyState.Success(result)
|
_state.value = PharmacyState.Success(result)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
_state.value = PharmacyState.Error(e.message ?: "خطا در دریافت اطلاعات")
|
_state.value = PharmacyState.Error(e.message ?: "خطا در دریافت اطلاعات")
|
||||||
@@ -85,8 +96,6 @@ class PharmacyViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun retry() {
|
fun retry() {
|
||||||
if (currentGenericDrugId.isNotBlank()) {
|
search(currentGenericDrugId, currentBrandIrc, currentProvinceId)
|
||||||
search(currentGenericDrugId, currentProvinceId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user