Pengambilan Data KFA Pengiriman data medication dan service request

This commit is contained in:
meninjar
2026-05-04 03:24:01 +00:00
parent 24d25aa14a
commit e8a0642291
80 changed files with 1718354 additions and 263 deletions

No files matched your search

@@ -1,22 +1,26 @@
package medicationdispense
import (
"database/sql"
"time"
)
// MedicationDispenseDB merepresentasikan tabel data pemberian obat
// TODO: Sesuaikan field dan tag `db` dengan tabel SIMRS Anda
type MedicationDispenseDB struct {
IdxPemberian int64 `db:"idxpemberian"`
IdxDaftar int64 `db:"idxdaftar"`
NoMR sql.NullString `db:"nomr"`
TglDiberikan sql.NullTime `db:"tgl_diberikan"`
KFA sql.NullString `db:"kfa_code"`
Qty sql.NullFloat64 `db:"qty"`
IdxPesanObat int64
NoJual string
IdxDaftar int64
PatientID string
EncounterID string
MedicationRequestID string
MedicationID string
PractitionerID string
JmlhKeluar float64
Sediaan string
PreparedDate time.Time
HandedOverDate time.Time
}
type MedicationDispenseSyncLog struct {
IdxPemberian int64 `db:"idxpemberian"`
IdxPesanObat int64 `db:"idxpesanobat"`
MedicationDispenseID string `db:"medicationdispense_id"`
RequestPayload string `db:"request_payload"`
ResponsePayload string `db:"response_payload"`
@@ -2,30 +2,30 @@ package medicationdispense
import (
"encoding/json"
"fmt"
"time"
)
// TODO: Sesuaikan dengan payload internal API MedicationDispense
func MapToInternalAPI(dbData *MedicationDispenseDB, orgID string) map[string]interface{} {
waktu := time.Now()
if dbData.TglDiberikan.Valid {
waktu = dbData.TglDiberikan.Time
}
return map[string]interface{}{
"medicationdispense_id": fmt.Sprintf("%d", dbData.IdxPemberian),
"encounter_id": fmt.Sprintf("%d", dbData.IdxDaftar),
"patient_id": dbData.NoMR.String,
"medication_code": dbData.KFA.String,
"when_handed_over": waktu.Format(time.RFC3339),
"quantity": dbData.Qty.Float64,
"prescription_id": dbData.NoJual,
"patient_id": dbData.PatientID,
"encounter_id": dbData.EncounterID,
"medication_request_id": dbData.MedicationRequestID,
"status": "completed",
"medication_id": dbData.MedicationID,
"practitioner_id": dbData.PractitionerID,
"quantity_value": dbData.JmlhKeluar,
"quantity_unit": dbData.Sediaan,
"location_id": "bfffca38-ce1e-41f4-ae40-f614d401443a",
"prepared_date": dbData.PreparedDate.Format(time.RFC3339),
"handed_over_date": dbData.HandedOverDate.Format(time.RFC3339),
}
}
func MapToSyncLog(idx int64, fhirID string, reqPayload interface{}, respBody []byte, status string, errMsg string) MedicationDispenseSyncLog {
reqBytes, _ := json.MarshalIndent(reqPayload, "", " ")
return MedicationDispenseSyncLog{
IdxPemberian: idx,
IdxPesanObat: idx,
MedicationDispenseID: fhirID,
RequestPayload: string(reqBytes),
ResponsePayload: string(respBody),
File diff suppressed because it is too large Load diff
File diff suppressed because it is too large Load diff
File diff suppressed because it is too large Load diff
File diff suppressed because it is too large Load diff
File diff suppressed because it is too large Load diff
@@ -0,0 +1,306 @@
3384146|dfa6149c-b633-4945-87ba-bedf68b06e04|fea2ba5e-297f-469b-a334-895deb024373|581e3187-ac4a-413c-ae85-39560c300f4f|3f6d19bd-1a32-42af-ae57-cfee8290dfbb
3384147|b569211c-92e3-49a8-a5e0-c50a99b91ca0|b6350930-85a0-46e1-9f80-7c7e5b942941|a3109162-df95-4581-9852-0cc0cda1372a|3f6d19bd-1a32-42af-ae57-cfee8290dfbb
3384170|abc848b2-9e1a-4178-85b6-a6d768a3e23b|062ca5cf-b30f-4748-9a30-4d4b9d588d20|2634b8d9-7ab8-4556-87d9-118d82d281e7|c3278469-0700-41c3-b46a-cb720de87e3c
3384175|405da304-2db3-4fdd-9263-910cbfca42e0|d0fec61b-d89b-4faa-8622-6eef70166b0b|3842777d-ceab-43ab-9a2e-08ff0397d7ca|48dcbabf-838d-47fa-a3da-0546e9499783
3384177|88bf8220-1c1e-4611-a836-520eeda3cea7|2ac3a8f1-48de-4452-8872-2028b3440642|18736b7f-fcb1-4e92-ae78-7073526ee605|48dcbabf-838d-47fa-a3da-0546e9499783
3384180|f054bf9d-a801-4eb8-97d9-f19ea1b1eb01|a7210038-cfba-460d-b697-29f018184266|b70c06f2-f06c-43f3-9ae9-ff1f5d7cec92|48dcbabf-838d-47fa-a3da-0546e9499783
3384181|ca36aaad-c799-4fa7-9a02-a5bce3d55098|55638105-b826-482b-b722-5999e7efaf88|8c146256-a004-4d37-97ff-5d0efc07b28d|48dcbabf-838d-47fa-a3da-0546e9499783
3384186|77953fa9-5f02-408d-9d1a-7b6e1534b929|6a36bc34-7d85-4d84-93ff-dc466a9d7cba|e5d36d3d-01f3-446b-b558-5c86712e1873|c3278469-0700-41c3-b46a-cb720de87e3c
3384187|41236af5-b24d-4b2e-8d69-5305491b8604|8c94e256-56e5-41ac-a9d5-326585353758|4a6549a1-e4c2-4165-8e5a-7a9cd068a522|c3278469-0700-41c3-b46a-cb720de87e3c
3384190|7012430b-1147-4471-ad83-7e1bd5c6d29b|c8c9625a-217b-4a88-b5c7-abc38ede1824|49e2481c-d370-4367-b070-d2c652b1c325|c3278469-0700-41c3-b46a-cb720de87e3c
3384193|ae0659f0-6729-43a4-b803-4d6aeb5cbc42|c0a82b89-0d39-4add-b024-a2e4b0861246|ddf97d42-c953-45d9-b763-e7072665ac84|3f6d19bd-1a32-42af-ae57-cfee8290dfbb
3384194|d75bb82f-3f63-4253-a08a-6b0d1b9cb030|4f5b3c82-8370-42bd-b6ff-f96b36dc387f|3da3d066-ca09-4fc6-8e83-5bde3c8a1d76|3f6d19bd-1a32-42af-ae57-cfee8290dfbb
3384195|d9aaaf89-18bc-4261-87a2-ff08a2f0d627|839d41e0-b4b1-49d1-b7fb-94a5bf07e0fc|2bfef15f-2c1a-4c0a-a598-34f229139556|3f6d19bd-1a32-42af-ae57-cfee8290dfbb
3384196|c8a123ee-6ba7-4a03-9d49-fd1239f81bc1|54975f8e-9d81-4f46-ab2d-df90e7c8059e|e9b94407-a524-4d0f-8efb-5e13080d4e2e|3f6d19bd-1a32-42af-ae57-cfee8290dfbb
3384197|81afa2ef-636e-4cca-9fbd-fb61a647a5f2|893b6e91-652a-405f-a003-952f432182cf|c6799b62-8777-488f-8931-dc1c820adebc|3f6d19bd-1a32-42af-ae57-cfee8290dfbb
3384215|40dad2e7-0d02-4b62-9ef2-eff70bcda462|90524058-c892-45c6-b6e8-015104bea41c|7d2ecbe4-5553-404b-a732-7d15b7cf9355|283df48a-90b4-4d5b-9c80-bfcbb8f84c5d
3384230|8a495b22-4084-43e3-9b06-bf0d959a594f|a5f442a6-bbbd-4f50-b643-5631cfce7d9c|f8d15006-08aa-4fcd-9f25-8921cef87860|13044dcd-c0bd-4068-9cd8-eba69f6a4ab7
3384239|ce2e9efb-af3e-4b60-92bd-84dbb9eae775|19364e8f-186b-47ee-9f74-14c66193f278|f8f506fb-0c8c-493e-8e2a-8401a064458f|2ea58207-01fe-4c89-bfd5-d008f53396e4
3384258|98edeb9e-eda7-489d-b854-dc0408e3e86c|b0ddc9c8-09f7-4d39-a2a1-125c03e7982d|5153d58a-a487-4201-b198-b3db83d47568|bcd2565c-6e4a-4f09-928e-dce241940759
3384260|71c9714c-4481-411e-838e-fd56cca6eecc|98139e99-cebd-4ad8-a9cf-17d702fe32f1|28a28511-a1e1-4c75-aba1-97b67ab0bb39|13044dcd-c0bd-4068-9cd8-eba69f6a4ab7
3384261|c426b6f8-82f7-4ff6-9c95-5de33a4c8955|c8dbdd89-ba82-4abe-8332-1861ae5d940a|f29fa145-5199-408b-9512-8184d5dbfa1e|13044dcd-c0bd-4068-9cd8-eba69f6a4ab7
3384263|e47cef11-b419-49ed-8ff5-8106026f550a|f11f8e04-6739-4934-b6fa-6ec8997e83e5|2a621669-e2d0-444c-bd2c-4a77a65be14b|48dcbabf-838d-47fa-a3da-0546e9499783
3384265|0e6fc5f3-cde8-42f4-a913-c484207fdd02|2bfd8695-1e7f-4572-81ae-d24e33976bae|9d61147e-0edf-43ca-b93e-f9b6e546e173|639d3256-142d-49fc-84ce-39d8d009bab3
3384277|7147ae06-d6b2-4d31-b13b-782c027424ed|bd2f2709-9304-4abc-a94b-1c9c0134a549|84ce68b9-cbd9-4cdb-bc48-81c3068f2d9b|8b4fa910-f774-4a93-88e1-3078e55bbed2
3384289|bdaa6b04-a246-4a6a-b218-892f8dee1868|056ddea3-e094-4432-b48c-07cc4b98e918|1d946f71-a050-444d-b97b-ffac87af3bec|d55df126-2bd0-4b7a-9ea4-5807d8db3edc
3384291|8aab4167-4c65-4874-a3c6-0abfb53faeb6|bb2f48c2-6cf0-486f-925a-ded251ab37ff|629ae24f-f117-4adb-8122-5033739f9a77|d55df126-2bd0-4b7a-9ea4-5807d8db3edc
3384298|846f1c24-05d9-4284-88a4-7282e0492271|e413763f-f9a8-475d-bc11-4f29ebf17d5e|e6b9a50c-1d23-4fdb-ada0-c79e605cc545|eca2d74d-0cf6-4669-bdf4-ac97060e2766
3384299|91d9ba6c-e738-4bbe-aaff-35db9ac3513b|ca5d175a-266d-469e-8b45-d50db0f21f3b|a14e9b01-5043-45a0-8d8a-bda67707d30c|01e81477-cd5d-4f3c-a21f-9862e66dac4b
3384300|531fc1e1-a609-4ab4-a5cd-99b4680512f9|d2d6248b-c86a-47d7-9249-618fc0feccfa|add6ad7d-e32e-4840-a565-49c9bba605b9|01e81477-cd5d-4f3c-a21f-9862e66dac4b
3384304|725908b8-b604-43b7-88f9-5ad9878c5f1b|f9ac1f4b-d2fa-4ea3-8517-d36962a48e6e|50d251d8-7208-419d-a8bc-84ad979f17d8|01e81477-cd5d-4f3c-a21f-9862e66dac4b
3384311|a3704bae-7218-4976-ad08-9378db1c6069|f87f3c35-95f0-418e-ae48-8bdc0e0339d2|38621f80-ae46-46ac-8f62-4bc7b66c2776|e423b1ba-8d61-4eb1-ac27-4c88c9e6eba8
3384314|f946e12e-43d1-475a-926c-0830c932926e|b2c06962-476b-4dbb-a857-3187e3c711be|5d978686-2e70-4ed3-825a-e7ed18f400e8|e423b1ba-8d61-4eb1-ac27-4c88c9e6eba8
3384315|296674d2-a439-4650-aac0-8d23e6dbe944|2705efbe-a014-4c1e-b73a-ac5e8efdf2ec|7d5ef2c5-5079-43e5-b0ce-5284f51006fe|e423b1ba-8d61-4eb1-ac27-4c88c9e6eba8
3384316|3157a6b6-bd51-46ad-9beb-937556d418f4|80962901-efa9-4fff-9901-2cdb6b34cf29|d789fe64-cf62-41ac-bbb6-227c23d90650|e423b1ba-8d61-4eb1-ac27-4c88c9e6eba8
3384317|0b2898df-0503-49af-84bd-f182817cb23b|86968a81-31f9-4aee-870b-bef03f33afae|a1b6f81e-a194-4b23-bd84-9cf249d8a40d|e423b1ba-8d61-4eb1-ac27-4c88c9e6eba8
3384321|218ff701-281e-41af-b661-bec0c4b4a944|4f4508d9-ce59-4f9f-b704-f396ad035e53|f56e3cce-7bfc-479a-8bb0-c2a5e35ff956|70d26e13-44b2-4e11-963a-05d5ee45a4a0
3384325|8b4f6d60-44a3-4f41-8aaf-494067f70870|447abb77-1f6c-458f-a36a-1217c105e34c|919c9238-965c-40aa-81ae-7374d178475e|dd476d77-1fe4-44ce-a204-0f071723b0fe
3384330|3d09a1df-ff20-4b4a-9197-abdc952566d7|fd7534e9-e2e1-4695-9c2f-bd48ef908efc|14b64e7b-fdd4-4e95-9152-54ba88fb1f02|a0290bb6-95cb-4023-b4f6-e6aa4b14e9d1
3384331|a1e1164b-b53e-45ac-868e-5499430592fc|32a673eb-f7e5-4012-b9a0-552ac7806e92|a73228ae-6021-41ca-9edd-61fdd21647d4|a0290bb6-95cb-4023-b4f6-e6aa4b14e9d1
3384408|2bc58cce-565e-4de6-8b85-78e951f89a72|ac0ebe45-b04c-444c-9397-bd9212e6c54c|abb1b18a-6915-4999-b016-68366d96be2d|31093962-f84d-4887-b708-83b24c04e5b3
3384416|a5cd2fb3-d87c-4d7d-96a2-61bcf480ffab|bf209d2a-e9e8-4859-b172-a8a0bfd76b9a|e3921a30-dc8f-4085-a5be-ce24c077c768|885b1c80-7c34-4feb-ba57-3d2e68549a11
3384417|9013ca1c-281e-47ad-b591-f9725dc69796|cd3f805c-41d6-448f-b12f-bfbc2c0c721d|7aeaec56-fd32-4d19-b561-5820c86eb803|885b1c80-7c34-4feb-ba57-3d2e68549a11
3384420|59f3de0e-f967-4ff5-9254-eb444ca29fd2|25a99e79-aabb-4790-b496-054016f5e53b|921bcc71-d632-4156-8a6f-7d79d887520a|a407c897-6bff-4303-a7f9-66d1c8f8afa1
3384422|fd1d0352-cfe5-4071-8761-adc5bc36f4bc|95f97f1f-4166-4fbe-a530-f3de8b78cb21|056092a6-bec8-46b4-85db-2e5f9da4b628|a407c897-6bff-4303-a7f9-66d1c8f8afa1
3384423|444d3a52-b92a-47de-83bb-6f6c91ffb624|a7a5f125-d015-4bf3-b6a3-eb8161c94af6|d8026a01-5b2f-423c-9042-577cb3221d8c|a407c897-6bff-4303-a7f9-66d1c8f8afa1
3384425|a1db0cb5-1a49-4b5d-9c1f-eef356c303c3|4339c50c-6605-452d-8951-e570a7299bd3|01adcc5c-349c-4381-9125-06816fa19034|a407c897-6bff-4303-a7f9-66d1c8f8afa1
3384426|62ddba76-e00a-44c6-af63-11ae027819aa|3c928723-e25d-4c9b-ad6f-1fd8b1e06862|0eb873f5-054d-4861-9b57-ba4cd45c33a4|a407c897-6bff-4303-a7f9-66d1c8f8afa1
3384427|6c1c96f1-3803-4dba-bd44-b3d40d661ee0|ddbc38d0-251f-48f6-b8e4-90b60c33c530|effbe803-2e15-4624-a423-595bdf670556|6a020cf9-4570-4fda-a33d-c577729b92ad
3384428|82f96d0a-5afa-43b5-bb07-14db077c44cd|5f61e03e-d622-4815-a87b-0ded41418e27|ac8a89d3-3c82-468a-8b99-5b4975d30b46|6a020cf9-4570-4fda-a33d-c577729b92ad
3384442|4886af9e-bde6-4290-ac8c-945de1ecfd84|1b0ee4fe-54cb-4ccd-8431-79f10f1c14f8|6dd478f9-9d3e-4478-af15-a9aed00cee8a|4b905f61-f937-471b-9976-cf3cb37da012
3384444|e49220b5-c05e-4aee-bfdb-3ed35226cf71|9064a14d-3316-41d2-bfb1-e0c8ae6c870a|f75da4cd-1847-4f95-a1d2-e2b3a58f71e9|3eb72fbe-44de-42c4-bb34-a8e72f54172f
3384445|904f8e85-65bb-4337-aa3e-55753dffa3a5|e64346c6-0425-4bff-984b-953d7fcee6ce|50800e76-b7b1-4d62-afe7-dada10212ace|3eb72fbe-44de-42c4-bb34-a8e72f54172f
3384454|6ca8660a-cdae-4786-85b7-b02c305dcb5d|9b6fbf57-7579-451d-a428-b3c0f64bee96|2c0291d0-d609-419f-bb6a-1adafe71d685|0da59159-71bc-450a-be3f-a211b36df747
3384456|ec8e1344-1251-4f4c-9d28-854b0e4720e1|81141e5f-a4df-467d-9fef-74e29f1bbedb|88e3ea14-2027-482a-b928-8ecbb773bfff|0da59159-71bc-450a-be3f-a211b36df747
3384459|76235075-0fb3-40d3-81b5-5607f80927d0|fd15f0e1-72c1-4cc0-9de2-18ba149366c3|41ab8bf3-bc2e-4d52-8933-b269669370fb|abcf7033-10e7-470a-9c2e-4b31e54a7716
3384460|4854d450-676b-4a80-a98e-d3dd245ee96d|b252a0ae-4dd5-4baa-8240-9977e9454bff|f82235cf-69ad-45a8-9318-5053cce14d00|abcf7033-10e7-470a-9c2e-4b31e54a7716
3384461|4c7d78c6-ea1f-4631-8e25-5a501215657f|f723b656-53b7-44a1-9ff5-1479489b1668|632da84c-c45c-46b1-854c-4a34cd60ddd2|abcf7033-10e7-470a-9c2e-4b31e54a7716
3384467|f5a3b525-01de-4883-b53c-1628ce0a7650|84389bde-8d21-4f79-82dc-0dd030da230c|96beb57a-67b2-45a5-abc2-f5550edb0426|4a119f64-733d-4551-8253-d850f7b251c8
3384468|9e543274-753e-4d2e-b137-f2b22b5e56e6|3eeadd2c-695b-42bc-9f29-04ff47524463|d43c2f05-33e9-442e-9b48-7d0f5f17454a|4a119f64-733d-4551-8253-d850f7b251c8
3384469|715c78cb-4a0c-41d9-8de3-ee0fc2cef4cb|fea73823-2dfb-4842-b1c8-40184f37eaed|5487d5c6-4213-4674-90eb-d0410dfba7d2|4a119f64-733d-4551-8253-d850f7b251c8
3384470|3ff5ccdb-b19b-405e-aab2-5fc04eb34762|ce50390f-67cc-4e41-ba87-3c40887b01a3|50e2f103-d491-4644-8316-715711a3aa99|4a119f64-733d-4551-8253-d850f7b251c8
3384471|27a8ffff-f539-4fc6-81aa-f3691781c9f8|54b0e2d2-2ee1-47d6-b361-511cae94570e|ca217f91-315a-454d-aa49-85454e30e66e|4a119f64-733d-4551-8253-d850f7b251c8
3384472|8847fca6-ccaa-44d7-95b7-2fa4bcc8bfce|b627d515-940b-4c80-bbaf-ceacdc0e4d7c|df86b609-92a2-4ecb-bb49-8780f5f3061c|4a119f64-733d-4551-8253-d850f7b251c8
3384473|851beffa-742d-49bd-b4b0-9c1de11065bd|0a13b531-136f-4221-9ba4-a63f89fcf9de|4141ed35-9cb1-41c7-ace3-abd137c2fcd6|4a119f64-733d-4551-8253-d850f7b251c8
3384475|203beb5c-13b6-4910-9b33-0fd4ae086e53|4bcdfa84-b455-4463-bd42-b211304afa85|18ae8627-c025-4f4a-8522-2dcfa406e336|08c894f4-269e-49d7-b5b9-844f7a6a8e96
3384493|bf3a39ef-e0d2-480e-bc33-cb67679bffbe|a956b1f9-0d53-4e94-b257-561a2f1f5c60|c486b8b7-395e-41fd-852a-1cf232d70522|657700c8-dcd7-4204-b65c-e84ed8cabc62
3384495|9a3db599-b247-4167-b100-b14600d89ce4|5ccae562-eac2-4fcc-84e2-36f03e6b3fcc|b4c86fca-588a-4e74-affe-c53fa15c6aa6|008fe409-50bd-4b70-982a-ede7acfd355f
3384496|ee8f26b4-207d-4aed-a9d0-b644f7300bbe|bf1abfd8-2df8-422e-907b-b0d1d0d9cf3d|584f583d-98e8-4ebe-9847-f71555551266|657700c8-dcd7-4204-b65c-e84ed8cabc62
3384570|5b595437-7449-4c4a-833b-55c8bb08364e|dce14e23-c291-4ae1-a3d2-326537873e9e|c55135d7-25b0-47d5-b58b-f438a911fdb5|7ca682af-edd4-49bb-83b8-bb0bb5dfc547
3384571|6b0662dd-0290-4f6d-9c0c-ae1f9b5b6079|d1aa9f83-bf61-4660-8110-02f88f5bbbfc|e930b975-ca19-47ef-a1ad-3c39cdf11bd4|7ca682af-edd4-49bb-83b8-bb0bb5dfc547
3384572|7d13385e-a751-41a5-8769-8bbd4ab73151|89abdfc3-3375-4a70-bc12-db386fddce80|27d880a4-059e-4670-9746-f4367ba7ee8d|fc97e0ed-7e96-4ea4-8793-837f354d1cc0
3384575|1c8569b5-d464-4b9a-af34-ab40587aea54|9fcb2274-8ed3-482c-8561-0b6ca43792fb|65bbeff2-50ce-4836-bc2c-1675184be5f1|30a7e945-2601-450a-9efc-0adfd7521e02
3384576|af9d200b-ddf3-403e-a34e-73fc12ca3e8a|3f8d5bdc-ffd1-4cc7-ba5d-0ca5a3f7c737|2b7dacf2-6c5c-4033-9497-4c6be2448f26|30a7e945-2601-450a-9efc-0adfd7521e02
3384578|72cece44-3ae4-4b8a-9bd9-0361e552a15e|318949a3-e8ff-4c5f-810e-3cb5501da715|c98520ec-79bc-4f4d-b819-6a5c0b89b127|30a7e945-2601-450a-9efc-0adfd7521e02
3384579|86e74f27-ec7f-47f5-b86d-3ca4f7a4d29f|c346d89c-884b-47a6-9b07-a4ca673eb486|39e4a641-8769-47ad-ae02-e395d675bed5|30a7e945-2601-450a-9efc-0adfd7521e02
3384585|de88b735-3f9a-4399-a63d-5f18482c5959|3a9490e8-8cf3-48c4-a031-c7687c96a934|674c399f-f0c0-423f-a15b-fcc7364df8bf|08e1b534-4bf5-4233-b4de-594853ff6530
3384587|8a167727-4036-4eef-9c3a-d893ed9e871a|09c5c09c-e7a9-4b26-91ba-6f1856eaa83b|3248259c-57f0-4c53-aec5-bf524deba7ec|02765825-d329-49c8-bfba-1af33daff7fa
3384588|0007c817-9b6e-41d6-b7c3-d27a34564a11|d8986324-7ff6-4340-8e47-da0fb2d579a1|e57dab2d-de73-4f26-8e53-275ad638c2c0|02765825-d329-49c8-bfba-1af33daff7fa
3384589|94e7b3ff-6d81-4f36-8d2b-2b2fc345edb2|4145df8a-8f3b-4b8d-bfe5-c01b260aa6b3|88670a75-456f-4a2d-8ac3-1db8e49ab3bc|02765825-d329-49c8-bfba-1af33daff7fa
3384590|1063ba4d-884a-44b6-b0ee-4f2bc4f1ce15|a0d8709c-526d-4189-a33f-f3d553795726|6e490fef-67c5-4c85-acec-7fa3f85242fc|02765825-d329-49c8-bfba-1af33daff7fa
3384591|a7f16354-de50-453d-8d27-acd779db7477|dad15c6f-eb90-4b4c-86ae-384a64c1e6da|4e54d081-b98d-4c88-bc80-99545849dd07|02765825-d329-49c8-bfba-1af33daff7fa
3384592|b63f5c60-bc3b-473e-a879-775de36edf77|e7d46988-3f16-4302-b83b-e76cefd44f99|3b5856d2-f5d1-4f25-a83c-fd1aa3b2b801|02765825-d329-49c8-bfba-1af33daff7fa
3384593|5c988ecb-9137-4a55-8622-0c1c9ede3516|9be2db1f-377f-47d5-8330-e30b98ae6c85|0fca32ef-eff1-494c-b67e-4504a2f6bee5|02765825-d329-49c8-bfba-1af33daff7fa
3384594|cba9a310-41da-46e9-9a7c-9dacab171158|f59cb3d0-879f-4ff6-829e-399f59870a3e|caeb242f-0199-4ce3-b2b3-aef3a1235b0b|412d04d8-8d25-4c4f-887f-a6d300a530d8
3384597|31acfc01-e075-4cc9-ac9c-60c40ce54454|4087aa01-e596-4cdf-bc58-5718770b01cf|304e5b2e-d2c7-4d03-84c0-6349d261bda0|91961f9a-15d7-4919-9a11-ea15733a2a44
3384598|30f069cb-7687-42a8-b6be-1f3e41e76756|48c65f6f-56d0-4911-9ede-16b4fae74ff5|7ed7821c-9801-42a7-982e-232c371f7f66|91961f9a-15d7-4919-9a11-ea15733a2a44
3384599|d060822c-6527-4233-8695-71795705a902|868eddde-51ae-40ac-b760-bf62864f3f7e|28852dd8-402e-4727-a626-3317157a61f6|91961f9a-15d7-4919-9a11-ea15733a2a44
3384600|126976cf-5c71-4d44-95c8-3c572582a861|1c6b99ec-102f-47c5-88b5-f145617bf5fb|d555d283-eb9f-4735-94ae-727efd0cfa8a|06a4fd86-a9b6-4063-b7d9-f7532cc82733
3384602|5ad02b16-4087-4038-afc4-7b75e6d9e5fe|9f4a8875-377f-4285-9cdd-173999341f9d|f59b6899-fe55-48c2-90e1-a8b3755ed19f|528c4827-97d6-4fdf-878a-fd2c524bce98
3384604|c2fc652a-e819-40e4-bf2f-b72b5bfad5ab|a2c7ce56-fcd0-4f48-896d-72fa90244744|8fb8163c-ed36-41b2-81d4-61aec0548144|528c4827-97d6-4fdf-878a-fd2c524bce98
3384613|726ea692-019f-4892-be7f-ff9127e787d8|9f6df6e9-962a-42ea-8d25-3b5d9b2df119|e71f21b8-b463-40da-9558-725e532e5b05|528c4827-97d6-4fdf-878a-fd2c524bce98
3384614|50b1c4ac-7d09-4ea4-a7b3-3056db9dbc08|ce8955c1-78ee-44ee-a6be-e475badd0c4e|e57d00f9-2ed0-40c7-b59e-0811893fa220|681578d4-4f0a-476f-9c3e-8db68553b69d
3384746|5657b135-3b73-4c72-a135-7ed71ef82bf7|c4c38b5f-3e56-431a-909e-e218462dc56d|21b018e0-4aec-490e-ad4c-830d926071b1|21a5032c-6ffd-418f-b50d-3af2cf860639
3384747|4fdec764-3d07-4880-a6bd-80c7337b240e|442dcca5-a25a-43dd-b402-87cc5861a17c|18532634-e933-4394-a4ef-16231d490ac3|21a5032c-6ffd-418f-b50d-3af2cf860639
3384748|41d613ba-7cc6-47e1-8fed-abba8048f92a|fe04c140-fe9d-4ff4-a036-76b75ef0f95a|07d3f432-34d0-4659-b924-6e208d95956b|21a5032c-6ffd-418f-b50d-3af2cf860639
3384749|86e7f57b-38ff-4d5b-89eb-2d061ead5d12|67b2c2f1-afd6-4e31-858a-82db9cfc004f|4f3c2100-0534-46ba-bbc8-d93bc4b0dfc2|21a5032c-6ffd-418f-b50d-3af2cf860639
3384753|83e6d8de-da09-4ae1-80ef-3afad9989c17|f48f5b87-d980-492e-9782-2cdedaae192d|4aed92b1-349d-4b76-b01f-fea43a48eaf3|102ceb79-2632-42cc-923f-1d0f62e4164d
3384754|d09c6b43-1765-441f-b44b-b1b10ed4e53f|78574f33-fc07-433f-bdcb-bcbb0521c240|fa99299c-3391-4c52-9d7d-db25218aa96b|15a03401-8452-4746-8a1e-1446bcf9f610
3384755|83bd0c64-26da-4453-b1e1-10f046fe0369|a6905978-a779-4d9d-90fd-cce7658d1865|708cb94f-48af-429d-86be-7000bca2bbab|15a03401-8452-4746-8a1e-1446bcf9f610
3384756|b34d4fe3-8816-4c1c-90d3-b0cc5e1a0798|f04ab53a-d8df-4825-bc96-f259f09c078e|ab067dea-9610-4f0c-8d93-84f39849a326|15a03401-8452-4746-8a1e-1446bcf9f610
3384759|500ea09a-4c6d-4931-9ce4-e329d66eab18|f72b9d35-c772-4fc1-a56d-1ee08bde0353|5edda0cb-a85d-4465-80af-c64329af95d6|8c95d10e-cead-4bdc-b898-0bb63575bfd4
3384761|269a9957-fe00-43af-bafe-7caabb557852|7e68a5e4-3a5f-4a32-958a-cbcae21fec4b|efd03b5c-798d-499b-aea3-c96beab117b7|8c95d10e-cead-4bdc-b898-0bb63575bfd4
3384762|98aeeacd-dbda-447c-94df-262011ac77a8|b6399efe-f1a6-4d92-9ccd-8c73d989831a|533975d3-7fb4-40ca-bb79-01671545d24f|8c95d10e-cead-4bdc-b898-0bb63575bfd4
3384763|d99a6110-7d93-4213-b897-44b9108424de|a89ffacb-a776-4675-8238-e82fc743917f|402d6ad2-c53b-4fcb-973f-f9096754ec0e|8c95d10e-cead-4bdc-b898-0bb63575bfd4
3384764|8809902e-97ab-4073-b948-3beb2d29830b|9e4f769d-25f4-4d9a-8adb-a8d75a1083df|bf226677-1bc7-4877-a1f5-10b277a39e62|8c95d10e-cead-4bdc-b898-0bb63575bfd4
3384769|2aa0461f-bdc8-42b6-b94f-9c9ddb3f7bca|ebd2dca7-1585-47ed-abcd-f870afee3b24|7727deab-4c9e-4284-84b1-cf3d014254eb|59194fed-7b79-44a1-81d9-e71a7fbb1c34
3384770|546fb55f-714f-4f2b-8dcc-2e4800c8c594|b44e3615-ef1f-412a-8d39-35548e96e173|f130a5f8-ea77-4501-b60c-aa101c3ddd54|59194fed-7b79-44a1-81d9-e71a7fbb1c34
3384771|a578ffff-19c1-49a5-83b3-74f2c4d18cea|bd4162ca-64c1-4995-981b-b6ca0dbe3507|2fe9252b-837b-4181-b32c-304be32b6b50|59194fed-7b79-44a1-81d9-e71a7fbb1c34
3384772|6f6d80ce-18bc-4c44-bdc1-25985937b37e|69d9db42-3630-4511-a7af-ed30fa3e3c14|ec25d148-bf57-4989-873b-da26bd17999e|59194fed-7b79-44a1-81d9-e71a7fbb1c34
3384773|9458161d-75b7-4807-b808-2c083eb647ae|a73fa2de-ecec-467b-8da9-5beb56cebf75|6158dfcb-05b1-49fe-a365-b7d38245256b|59194fed-7b79-44a1-81d9-e71a7fbb1c34
3384775|2ce21cf6-0605-4d8f-9fae-30ab6897b8c3|d6d2f1ff-7883-4c4e-8507-5dc1129f4496|dc1f8564-a80c-494a-8d43-8eca5c786fcc|cb486202-2d36-4ae3-9dfc-dc79dc8602a3
3384776|986ed0f0-1759-4365-9f64-5cab788aca85|ac527461-7cca-4f25-810a-f2d34ba935b0|ad6a0831-d892-4f26-b791-7ae656c8ea27|cb486202-2d36-4ae3-9dfc-dc79dc8602a3
3384777|da30248b-8446-4871-a0f9-6e6fe46e0c01|e1038525-daf0-4426-bd02-a7aa6babdb19|337535c7-d835-4bf9-a9cb-2c525a59282c|cb486202-2d36-4ae3-9dfc-dc79dc8602a3
3384780|0cb55cc9-1e8c-4a8a-89e9-08f1115748fe|1e198d3b-131c-4330-bcdf-a0445c76d705|67ba6a07-4e75-4906-bedb-7e46b2bdb30b|e0fc254d-d8cf-4b52-a02a-76862e048d1d
3384781|59246f35-18e2-48db-9faa-fa3b69891870|58c7095b-5bda-449b-a87e-6e5a587bb6d2|98dc391d-cb30-4f84-a533-1cf96e94806a|e0fc254d-d8cf-4b52-a02a-76862e048d1d
3384782|286400b3-1ad6-4958-bd9a-4438a5c71ed7|d6a38043-7aff-4493-8826-9f8dc2cc36ab|c38129d7-662a-421e-8f4e-6d48513ac50f|e0fc254d-d8cf-4b52-a02a-76862e048d1d
3384791|217fd5fc-fe4e-48aa-b3b3-3c974d5cc7d1|874332e2-cdab-4c42-988e-0dfd051fc1b0|0f0665e6-4d86-4c7e-8f02-c62aebf61cd7|f6f09d06-78a7-431a-a350-05b57b5a9204
3384792|1f685f90-ac02-4fe2-9f31-3539403864e8|bc20a113-dc1c-47be-b69a-03335e1a0de3|afad0983-1e4b-4b46-b33c-dbb7e171815f|f0c51bd9-1eb0-4172-8f26-0007043bc5a8
3384927|0328a582-b47f-4d0f-ae9b-04a3b4e0a4d5|e527af8e-4006-497e-bd7c-595267759b19|65842b11-8a72-40ee-8ce6-b39063cea00e|3ae6afb4-9725-4b23-9852-b956733e7f07
3384928|04fb5d59-fe1e-4b01-a51f-e758c2fca92e|7bbcbe81-25a6-4715-8d97-74a3857a1e29|33b2ab94-6754-4d1c-a735-d53697b078fa|df7fe956-8864-4863-9bb7-eef5d288ec47
3384929|9d299c02-3298-4478-8e96-4046a1dc5791|4b464ec6-b65e-43c0-84ef-7cb24799f61f|02df370b-00d9-4caa-92a0-2ef824444602|84a83599-1145-42c5-b417-4cfc9ed0675f
3384930|defce960-bc35-49f4-ba44-763d1be38001|96dc3d40-127a-45b5-8580-f7d952aea44e|7d958fd6-0f14-4332-bcaa-6ecd529bafab|84a83599-1145-42c5-b417-4cfc9ed0675f
3384932|b02cdfbb-1c7f-4e3a-8b1d-922eaa84c657|cc81b412-b036-4ca7-959c-1d0767477a08|0cc3ee83-d3cd-4eaa-acba-2983e1a7d9ff|84a83599-1145-42c5-b417-4cfc9ed0675f
3384933|135ca34e-9a16-43bf-8555-d0bdb3db8ab0|e398003b-8fef-48eb-bb28-fcf786e1b429|1374b10d-efd7-4749-909e-cbdca1252c49|e5201c98-64f8-4ac6-9305-91c54c59a59a
3384939|5d0e7c92-0fbe-4139-8723-0e380cde602e|9e78db9e-9f63-4005-89ea-e319037fe48e|19ff8f69-54d9-4df7-ac04-602cc5bd867c|e5201c98-64f8-4ac6-9305-91c54c59a59a
3384940|ca59cf12-2b4c-4609-8d20-023c2e11af89|62405936-f047-4920-98d4-2d89df4e77df|ad8ff83b-d112-42b8-b8c5-0b9766e9fbee|e5201c98-64f8-4ac6-9305-91c54c59a59a
3384942|3ad465c6-f553-44c9-8a1c-174dcda50aa2|983f2241-8f1d-45ca-8971-d8059de466ef|3b158174-9110-4ea4-b667-4f99fb090a6a|060a239f-dc27-426b-8505-e82df4ab3972
3384943|18e39ff1-7166-400d-9934-e5780fb7abec|a0a25bf9-8bb7-4afb-9dd5-3558debf6a0f|eac42a79-934d-4cd2-abd3-b8fc54c1f0fc|060a239f-dc27-426b-8505-e82df4ab3972
3384944|4eff6135-ffad-437e-90f7-948806844a63|8145ed9d-0c9e-4337-8dfd-ea019b70d945|18eeb182-1357-479c-8710-054c0a9751b2|060a239f-dc27-426b-8505-e82df4ab3972
3384945|e04813e9-7605-4c08-b637-fc99ac1f8dba|35112678-427c-4355-b5dc-7674b91c2213|6a2ce5f1-298e-4c60-8eae-a52a2ae9ef52|060a239f-dc27-426b-8505-e82df4ab3972
3384946|76687eba-69c0-4032-bedf-0a2ab165e580|d0b4e4c3-14f5-4f40-ae4c-b18aca87da5d|42397c2b-4cef-454d-ae06-90faf6fe7dc5|060a239f-dc27-426b-8505-e82df4ab3972
3384947|d9966be9-0067-46ca-b9e5-6818d90039bd|1d9acf16-c680-4226-ab6b-f6186ce8ed65|5e0d053a-ff8c-479c-8424-60d356de69c6|060a239f-dc27-426b-8505-e82df4ab3972
3384948|ad333380-6cd0-48bd-b5d2-291aa9b3c443|6c7a3986-f559-4421-b84f-4d8eaa007113|352e1a58-8a7c-4407-81a2-4e5f6d639e84|060a239f-dc27-426b-8505-e82df4ab3972
3384949|c98651b4-c17c-48e5-b3cc-402fb451dca9|80e4700a-5a59-4def-ba73-a9c356fa8679|ae461891-5a85-45e7-a95e-51f3f0792076|060a239f-dc27-426b-8505-e82df4ab3972
3384951|ff264db3-ef34-4eef-9f15-026ecc02f7bc|b303419c-c861-4f36-be12-992593077339|e9736f5b-b262-4478-82ae-86b333742d70|4f1b168a-d204-4104-a028-9eb139e993b6
3384952|f5b28b2c-440f-4838-8924-5d571954b08b|9c728448-6926-4b94-9a0a-ed14cfa8aa2d|05a53a1f-0082-4852-a102-db966fbffec8|4f1b168a-d204-4104-a028-9eb139e993b6
3384959|c698bbfd-0ede-497a-86b7-bc2b060a82d3|36036f5a-7871-46f0-b3cc-85d1977ab162|4923acac-f127-4c16-b9f9-39cb87fdf094|18e722a5-61db-4464-bf79-5c08040bb7f1
3384960|cb4ebbe2-f95b-4b1e-ad35-fce0ae1c1d84|ae6d86f0-edfc-49a1-b85c-722a25b4f832|ee10fad0-92e7-4def-8e63-4d49f680ec01|18e722a5-61db-4464-bf79-5c08040bb7f1
3384961|6846a587-86aa-436f-a5df-2ae4df99632b|b8ad72ef-2ecb-49ff-9cc8-3f8edfbb3cc6|c30b65d3-87b2-4b1e-aa0b-fb05eef614cc|cf2368fd-f77c-45a2-91e4-0d5559a41a49
3384965|3c240f14-c354-44de-8756-18c32b284074|6e86d4f5-9bcb-4317-8a1f-7c12a27e44ce|9e33e7d2-a7ba-4274-bf5a-b3b4e22c1360|8c95d10e-cead-4bdc-b898-0bb63575bfd4
3384967|a76384df-5961-444b-a471-f3bf7e4e4faa|5234ee1b-78e2-4605-85f9-670b23cfe1be|810efec8-59fa-4971-9eb5-ea6fd1eb06d0|33952375-7537-44b8-b119-9cffe03e3dde
3384970|3067f98f-4601-40ee-b285-20acb43ba4f5|79de8c10-930b-44be-8c91-1edb1aaadd31|ceb94e47-2d8f-4b4c-b2a5-59930199ad21|6ddf658b-c60e-4316-9f10-81837aea6b43
3384974|93cd07a2-6303-4808-b839-51e83657d891|30ed34c1-5bba-4fd7-9591-772bcddd119d|cf39632f-3bf1-43a6-ad38-103efc6ab564|7bcbffdf-48b0-4a57-aade-458aa029f77f
3384975|161c262d-1519-4186-8126-1cd9ed2ec59f|f28cb87d-f366-487e-ac5d-46691b93af84|d8e8d1dd-2284-43b6-92ae-1e2d0f51dbe7|7bcbffdf-48b0-4a57-aade-458aa029f77f
3384976|ec8725c3-7e8d-4986-94c2-0c3a96b5ce25|e741488d-d5a4-4056-a667-2ab27e1ed4c6|e89f7fed-cbab-42b4-8a29-41b678c7bcf2|7bcbffdf-48b0-4a57-aade-458aa029f77f
3384981|49e9a38a-e152-43d9-bb3e-56ae75b7b737|fac002c5-c564-4d99-a722-fd2120d49733|d3bd7020-a25b-447a-9409-44e87f00961d|11e6dac5-8bc5-45fc-9f7a-e840e3a00e2e
3384996|2ef52646-46c8-466c-94e1-5efbeb73ae66|995eca60-a688-455f-ab0e-e53f086085ce|30714ec9-f920-47ba-8686-8a44dfa955b9|b1ef547e-1f3d-46d8-88b2-6e4b3654caa8
3384999|a1b3589e-9294-4ad5-af41-362e157004d8|a02664ca-9034-4ef6-994b-dfafe5895c1e|2636c920-a9a0-4951-b334-f626d25b2637|0f4f817e-ae2c-44fd-9066-0bf50473931b
3385000|e5c83237-769c-45f1-9aef-420396a651b1|a49f2cee-e0c5-4d24-981f-a9ed66d468c7|02f2d9b2-0215-47c6-8efc-01c444fb3cbb|0f4f817e-ae2c-44fd-9066-0bf50473931b
3385001|a12d3222-29a7-4a09-948b-310e8d54a035|2c3c8c11-da7e-417f-8891-b3d86253eca5|1b964514-a2aa-4272-a2b3-2122eb0867e9|0f4f817e-ae2c-44fd-9066-0bf50473931b
3385103|2f4f0438-ca41-412f-9dc2-00d4b02325ca|fc7755a1-e79f-4e3c-bb82-884efeb60743|61d1136b-1729-477f-a065-a8d3af9c5c35|4b08245a-3a1b-4aa6-b23d-c25fc7887eb6
3385105|dfa0fbdb-8add-4f94-941a-58398a782275|6e5e54a5-5a1e-4cab-8d5a-276d61ad3216|07d5a354-6d05-40bd-a442-86a5df92ea81|4b08245a-3a1b-4aa6-b23d-c25fc7887eb6
3385107|e6e573e2-b070-4793-b466-d7d1acaa946f|e2530860-d42a-4ec2-9136-df864ae305ff|9319351f-a8a7-4093-9cb7-2f6cd2006e46|9e1e86a1-d220-4835-b58c-f6d65714f323
3385108|9f815555-5cbc-47a8-a1e6-163072c93454|d594f29a-d6f6-411d-8640-315818fa4dfb|9f3326cd-4039-47d3-b953-1e59ea0e6125|9e1e86a1-d220-4835-b58c-f6d65714f323
3385109|1b2b0ee6-5596-4d15-8c97-4ea19084b02b|fdc3d7ee-d979-4492-9209-6854ee62ed2a|b1c8b34f-1b0a-4e16-9247-33ba206286af|9e1e86a1-d220-4835-b58c-f6d65714f323
3385111|319d9bc9-41d9-46dd-bafa-3de90a83f6f3|49fbff1d-50fb-44d4-aa7b-cc86e68152c0|1bd10684-1fa9-45f2-93ff-6bdbf12876b9|9e1e86a1-d220-4835-b58c-f6d65714f323
3385112|6fe8b4ca-09c9-49b2-96aa-bcb464ed297c|91094ea6-4278-441e-b1d9-25da56887f8e|47bcf943-5a2a-4c0c-bd39-2d1982a9e7dd|9e1e86a1-d220-4835-b58c-f6d65714f323
3385113|bde760fd-1b6d-4872-8cfb-bd5edc860d6a|12aed7d5-ca9d-41e1-85ad-1248d1e5755e|c3737bf0-759d-4a21-9af1-b986a6c1bf12|4b08245a-3a1b-4aa6-b23d-c25fc7887eb6
3385114|f018b957-1091-48d8-9291-9f1ea5584841|94801b14-e774-4f82-bcca-2395dd1598b3|b115216e-5151-40a4-9310-c79d9b703840|4b08245a-3a1b-4aa6-b23d-c25fc7887eb6
3385119|83bd5ae9-ca26-459d-b212-102ce9615e2a|09f4e9e5-d252-44f6-ba28-5b78f33fdcff|70343a46-f92f-499a-a0e8-c583e959c3d8|89153fde-3ab1-49ac-9b6a-9f81f9bcf3df
3385120|0ffd7f77-f35d-485b-a6bd-d1aa0160cb10|dd5842fb-7400-4327-aee3-0430cde952ad|3ec2cd41-5a38-4ccb-8c29-4b5ac6f52d85|89153fde-3ab1-49ac-9b6a-9f81f9bcf3df
3385130|619e27ea-4e98-47b6-af9c-a9daef7984f5|7171179a-43ae-4b31-90a1-8743892b0972|3d550cc7-5802-4565-b41e-c6a3e93a0858|2571ed44-7a57-4a0a-8c4a-d5844e0b97fe
3385131|d5ba12b0-ad97-43a5-bfa3-406843d71902|7716e3c8-70fa-48a4-b18f-0d10fe7500ed|bba373f2-651b-4eb4-b5f2-889926639b3b|2571ed44-7a57-4a0a-8c4a-d5844e0b97fe
3385144|ef33de79-e540-400a-9939-86dd647dbc8a|cf361382-9c6f-4d49-a71c-eaf129ffb2c0|597f18d9-fcca-4e18-af0b-e5b63376c240|9c49b35e-3fd6-4a49-bfba-ff6850ad25b4
3385145|4cab0b4d-c489-4f98-a75d-076b364d81f3|8a6e651d-30ac-4c11-8625-24e942a345c9|a54a8d4b-6e1a-4d58-89b4-b334f977a44e|9c49b35e-3fd6-4a49-bfba-ff6850ad25b4
3385155|fa1e9a58-1593-4822-8e8c-9563968da6c3|dda542cc-c5e5-4397-909c-d93143602ae9|082efe20-f3e3-4f94-a45f-ec3cf2d13133|58f18ab7-2274-40bf-9aa6-87a1db1b6832
3385167|57886e04-d405-4d53-8ab5-24ab6faefeeb|93105d59-2bf2-43f9-9829-44dd5d3de0b4|7ebadff3-2a44-4039-a956-8e9f56c845fc|cc144a73-acaf-4616-a9dc-abd917db6438
3385172|736ffb07-2328-41d3-a7cf-b400e5051af8|8832c1d7-1fa5-43f2-95ff-849bcd4056a3|c67dccee-625f-4377-90ab-17241e1bae88|eae4acb5-37c8-4ec9-9b90-3605a88672e6
3385173|20718fae-ef2f-46d4-9a5b-397fce33c08e|5c81e989-edf2-4527-87bf-6263a6472d49|4df38271-6a26-49b7-84e6-d31fc8f0d19c|eae4acb5-37c8-4ec9-9b90-3605a88672e6
3385175|c107e561-ee85-426a-a2c9-efb64e6fbdfa|e84436ee-12bd-408c-9604-25a426eed282|ba9a01be-099a-4404-a0f4-71443ed9d32b|eae4acb5-37c8-4ec9-9b90-3605a88672e6
3385176|8fe957ae-1470-4d00-a659-fb05a20eaa86|78aba068-4e4e-40ed-9533-cf88788ba766|13d15b9d-9307-444a-b0a9-7dea6e9dfb12|eae4acb5-37c8-4ec9-9b90-3605a88672e6
3385177|d9a69120-9dc5-4d89-902f-657cbd05cd56|807f7aa1-5313-4ae4-b86b-a3c40f8516f8|c4a18df6-b173-4f10-a284-e867448cecf5|eae4acb5-37c8-4ec9-9b90-3605a88672e6
3385178|3c14aa60-2bba-45de-9838-ed8693aebc7c|318b6d35-9121-420b-a365-21c8b6fc86b2|16b7cf83-16b4-4cb1-a190-aafd5d5b220c|eae4acb5-37c8-4ec9-9b90-3605a88672e6
3385350|d0854ea3-69e5-4820-9f87-3cf31a7bddca|918b3266-60f4-40e6-9145-b1d1c1e8fe20|6f5cc792-18d0-42b2-abc3-9cf7900557a7|6fc7143f-119e-448b-b73f-96cfa54a73db
3385351|407da7c7-6f47-4087-90f7-5efe65a17f09|cfa703b4-2909-46ae-8086-c47f9fa0e829|65690f36-2a0f-4dec-aa12-b0faf624099d|6fc7143f-119e-448b-b73f-96cfa54a73db
3385353|733a0ebd-75f6-43a8-bcdd-e3c1a0a8745c|a8c065f0-aebe-4887-959a-290936e2b8f9|5b9b3c16-54c6-46e4-8b48-ff167092fc11|f2efafa2-ffe5-4019-a787-7b47a6eb77d9
3385364|7628b5eb-8c79-45cd-a558-0ce7f0d6941e|e291c6ee-8069-4297-bfaa-bbf359a4bc59|180e4c4c-5b89-4e5d-90b2-d561bd12f959|f2efafa2-ffe5-4019-a787-7b47a6eb77d9
3385365|26412e81-eeca-40cc-afb4-bc8af5fdd1e3|0e27cdb8-9fa0-4881-8ef6-027636533df4|d62169ff-4eb0-4866-859e-fb6d05479dee|2591cfc7-ac5b-4ae2-9f32-41444d046161
3385390|11e9c8af-181e-474c-9d4b-ef49921ee20e|28d73539-cdb2-44bd-b0f5-9db215254b94|9d3edbee-92d4-439b-b9a1-ec0c03582d6f|f6824855-2bd3-4b44-82b1-c44b6bd0fe46
3385392|26b642e4-5e44-4749-831c-9df069b03a87|535e6d9d-b8d9-4ec5-85ef-d8c9ee45dbdb|2bb89555-4737-43ee-ae17-b39873380fa0|f6824855-2bd3-4b44-82b1-c44b6bd0fe46
3385394|cc16e186-6c47-436e-9b32-1b0e63edf9f6|a8c4e9dc-41c8-46d2-9a0f-d22facf44800|084c46b7-b9c2-4969-93af-f4df43e1ecfe|294d3714-4aff-4b92-84f5-d5878610c683
3385395|ea7ed4dc-963e-418c-9873-eee0b48b1ec5|f40f927a-21f2-4bf8-8f31-a84b1ff9cfe8|87ba5cc4-fe97-4424-acd1-0d42b53187ec|a4b75765-1515-4326-8842-4ca4cad833c2
3385396|2d3bb4d3-d66d-4510-b7ad-703e30de2c98|be7d69b5-6e84-44ff-8a13-b22685ae2543|a13075a9-ec3a-42a1-a394-a57380b2ebad|a4b75765-1515-4326-8842-4ca4cad833c2
3385400|1062642a-395c-42f0-8daa-09ff5191740d|1aaaf560-db48-45b8-b6ec-363c29183adc|2a82d46c-b539-437f-874c-f92b1eeb472b|80a49198-ea61-4290-be31-fc25cf4d1b03
3385401|7c29985d-3aee-431e-a394-a0e76645b707|50c8976c-93d8-4b1e-8f7f-fc46d459dd8b|11c4ffb2-11bd-47d5-a2ec-fbdc2dbab561|80a49198-ea61-4290-be31-fc25cf4d1b03
3385402|b18bcf84-de38-4ed7-be8e-e2b8712647ba|05e75e41-2490-41ae-a2cf-0d725ab55fa6|e1c77a6a-9d00-4c81-87fd-fe451b01ea3b|80a49198-ea61-4290-be31-fc25cf4d1b03
3385405|303665a9-700c-48f6-9d78-dd6d9de06741|e690a414-f9d4-43f1-aa6c-cc67f4003f7c|4f13ea89-df94-47f2-8618-9f3a94351036|c3c96d14-60ab-4439-b739-c51f8e109e0b
3385406|ddcdef27-fa16-4f4d-bbec-9ee8e4d4b8ff|72b5b40e-bc1e-4003-9f6d-ee9bb1126fb3|0f031f69-4f27-403d-9e8b-6ba073b81f99|c3c96d14-60ab-4439-b739-c51f8e109e0b
3385415|56683f97-c141-4ae7-aa02-708e6afb8063|cc2ba3b9-f8c8-4c20-a2ce-1f26994b0975|12459edb-b339-4252-bd03-4cd8b39a58ce|d42dd038-7510-4ba5-8904-7922095ae6f7
3385417|5c2a26e0-f641-44e9-89f5-52c984b08d06|d76d8884-efe2-4cb5-ba81-68d6398737ed|f383afcf-8e7a-4d09-9fc6-a483a4877462|7eb6dbaf-649f-45e2-a1ce-0e420e1be7f9
3385418|12e26fe4-0ad6-4a1f-a871-35626356cb05|29f97c44-ae85-415c-bea9-85eb25cf83be|15223779-a86f-4161-827e-549d826a2ef8|7eb6dbaf-649f-45e2-a1ce-0e420e1be7f9
3385419|a1733bb7-312c-4727-ae12-17050ad2fcf2|a1a79274-392b-45a9-b260-76e6bbbbcfa6|ea4ca17b-2252-46c1-8ae4-1a6e748f63bd|0cbbe9f1-98cc-46af-8ff4-4e6e9bdfa91a
3385420|efa80bfc-f86c-4edb-aeb3-3508dacba6dc|b698252b-8625-4eac-8221-766441074998|c3bc8706-e029-4a42-a36b-58c3487e0348|0cbbe9f1-98cc-46af-8ff4-4e6e9bdfa91a
3385422|e39eebed-4ad8-40bf-91e8-ef51f6111ac4|802dcb7f-9db1-442c-b856-f017a60e5f52|04307aa2-b9f2-4896-942e-56705677a621|0cbbe9f1-98cc-46af-8ff4-4e6e9bdfa91a
3385425|75ad8568-e318-48a4-b1d8-4657913a5a33|d6891eb0-e203-415c-a925-5ac45757fc64|88a3a266-7094-41ba-a1b6-11de2bb011ee|a731a143-dd1d-46cb-91b7-9e338baeaf07
3385435|c84e3707-0474-47f2-b388-c796423b02a7|7eb3bdb1-8d76-43aa-934e-08684f8feaf6|da59422c-5768-47bf-9d3c-ebdb07c6814c|430410ea-aaec-491a-992c-9b3bf9813a83
3385436|57b3c6fa-432e-49dd-9502-47f95d61d96c|ad7be2fd-8d0f-4b3b-8417-75cc39313940|4557fb74-1dbb-4e51-a011-ab34e5e97b3e|430410ea-aaec-491a-992c-9b3bf9813a83
3385437|2e28b0d0-b7b5-4f9e-9a04-e055e859e1d8|00a09baa-343e-403d-bd49-305bb819f425|06a92772-4b53-4cda-83c3-440ec059f29b|430410ea-aaec-491a-992c-9b3bf9813a83
3385439|d41447ab-6aa0-40dd-92a4-c92daade072d|b81c1acf-f0c7-4f0d-a1b8-671842cef480|bb2d626e-822b-4b7a-92ef-c6330281dd76|6d890450-0615-4952-973f-efe5e958c8e6
3385440|3f4fdfd3-5a37-4e7e-9648-0fa16242a643|26337279-112f-4e0b-b9c8-3495a944c372|747f0c10-011a-45eb-aed8-b93e2311ca29|6d890450-0615-4952-973f-efe5e958c8e6
3385585|ce241ce8-293d-4221-bde6-e93fbee465a8|fb5c59e0-ca42-4735-a3d9-bc659ee5a8d0|bf3547a2-9901-4847-ba1c-afb471309be3|e7b4ba2d-e737-443e-9dfb-76fc9ff0fca3
3385595|ef537a64-e90d-4694-98a5-d2cec7f88ae4|2c8317d4-10d8-402b-8b7a-2dd17a3dd525|8b47eb57-f1ac-4a62-bfda-d8a7cc479c76|83c4a5f0-edfb-474e-8e8b-3bcf263e100f
3385604|4e44e308-07e8-4a0b-b8b8-1e56789b077d|4681d90a-6317-4c9d-921c-9fa2b8b84a4e|f72961f7-0dc8-4d46-bed6-11e8dde7d723|bf699624-08ca-4fb0-93c9-8b8ff20dbe8b
3385606|33985f34-66ff-4793-85f7-f7b4af27f584|1076bf42-1be2-4b1d-994c-295b50ac3dc4|07f7a96d-bb68-4c2f-b7b5-22bd75855d58|bf699624-08ca-4fb0-93c9-8b8ff20dbe8b
3385611|cf4eec24-8803-456b-b125-8192b352e8ab|a4f94b87-8ede-4301-94f5-22d4e437c242|16a7ac5a-edf4-4b1b-8ecd-dcdb6cf19b4d|bf699624-08ca-4fb0-93c9-8b8ff20dbe8b
3385615|23449302-a982-4720-b5ff-5cdaaadba1a7|02226f00-4c0b-4117-9b1a-4ed0a24491c3|788ab6be-77fc-445e-b2b9-89b26a4005f4|c7a6ceea-b9c5-44a8-9ebc-f062db148da4
3385616|46e5ff7f-ac39-4b2e-abb9-d3c6be5050b1|50434d84-0c3f-4eb7-9f08-ad05b11ce42e|ab65c9fb-d25f-49e3-a92c-b325213b6c85|bf699624-08ca-4fb0-93c9-8b8ff20dbe8b
3385617|d07d2fab-8720-46d2-a049-7e3c1c4008a2|aff2e9a5-f20d-4381-86c3-717a6e298ac8|9b4e358a-5cec-4711-94a6-f8e2e9e25c10|a2ecfca1-a696-4c74-9a42-0c92a67d3740
3385618|76b84d0e-1ffa-42e0-aa00-2d3e40d933b3|f2bb42d7-d923-4218-9110-b09348eb4c5e|82a982a5-07b2-4ebc-9247-5c9b5f397adb|a2ecfca1-a696-4c74-9a42-0c92a67d3740
3385634|0fed2e24-5615-4168-a423-5268ae30dbe7|118c4050-b9fb-4b07-be3a-e97beb996791|595de46e-7b82-4dc1-8bd9-3f8329a1d45c|96f79495-6c68-456b-ab11-0c58d5528cfb
3385635|53c6a93b-6012-4f0c-8b01-1e00f87c0952|1ef0730e-47c9-4a13-8ed4-88bc9a46e442|a54c3d5c-96f5-46f2-837b-e0d14c71fe1f|96f79495-6c68-456b-ab11-0c58d5528cfb
3385636|754981c1-a74b-4150-a072-d5565a8b0cbf|0a3f2151-a222-4e47-986a-060775dd5271|8d6b61a8-32e2-49b2-bd32-123cfea11581|96f79495-6c68-456b-ab11-0c58d5528cfb
3385637|6f32f248-f950-466e-84ae-5f480df53ef4|01e7d9bc-e19e-43f3-bd68-c10b779122c2|b6706b64-cdf5-4cc5-95e5-04627b1c5b5c|d3d3b4e4-bdd2-4df5-a6e2-8dd641c14a9b
3385638|44824b5f-902f-4bf7-bda4-173052dd1242|5df086f9-accb-4c6c-8eb7-9a3ac1aea43c|1fd2d1cc-12b6-4db5-9ea2-b53cf6c6eab4|d3d3b4e4-bdd2-4df5-a6e2-8dd641c14a9b
3385646|57b487cb-fcd3-41dd-8abb-f2bc87bcdd64|f2f252c5-78cd-4698-ba1f-c992ffbcaa6a|e045bcde-a1e1-4f7b-9c36-d071d7215e09|5ead6331-dd00-4faa-8c03-9d52a35985b6
3385647|4f55a24f-8735-42eb-8a69-27e4d45d1cf3|b1606b6d-1cb7-449f-aa0b-c64b8fe181f5|00a53905-8094-41c7-b89e-1c5a0c469fa5|5ead6331-dd00-4faa-8c03-9d52a35985b6
3385649|615a0acf-fb4d-4f8f-8569-b693d9dbba97|185e36ad-86e7-49e5-95d6-274bade75016|138e99e5-c2be-44d6-ad2a-32bf8a678d2d|5ead6331-dd00-4faa-8c03-9d52a35985b6
3385654|88133500-7336-4965-993d-474372aad2d2|d6479cf8-c7cb-4578-9deb-be5934119abc|a42f0341-66e3-4834-b2dd-d3e515b7af1c|18e722a5-61db-4464-bf79-5c08040bb7f1
3385656|df5ca051-d2b4-4585-8625-1bd4ceb31da6|6803f411-6435-44bb-9798-b4c5e392a2ff|382e220e-8e3a-432d-b0e3-2a939803259e|03c3f660-f3af-40c6-9b39-54eb3161be3a
3385657|9e4fc6d3-579f-40b9-ad38-4c6ab05c0cfb|224100d4-d577-4ed6-b5dd-7ef4aa2417d9|7432b7ee-f348-43f5-948b-1de6e8a5cff7|03c3f660-f3af-40c6-9b39-54eb3161be3a
3385673|612fb77b-f68d-4319-acde-76c87a6c4d89|4e04f76a-4e63-49b7-96ba-882619745570|9b3a5de4-1726-4cdb-aceb-0e744751a646|9c49b35e-3fd6-4a49-bfba-ff6850ad25b4
3385675|bad33424-da99-482b-912d-ad59dd454545|4a3882b7-187a-4365-ae8a-14bb7ea380dc|ab8e24d4-bcac-4373-9b33-d82fb54679ca|9c49b35e-3fd6-4a49-bfba-ff6850ad25b4
3385677|8f78f784-3921-4a84-9002-916990f8920a|26b3efc7-75aa-40cb-b6bc-624bbd287918|ccc78580-8c54-43bd-96e4-8ee4c01c2f3d|9c49b35e-3fd6-4a49-bfba-ff6850ad25b4
3385679|a7e5887c-606c-45bf-9f6d-b533ddddac1f|739580d1-8bf8-42ce-bb90-533410542006|b30f8755-860c-48e0-a774-0126d5c880bb|9c49b35e-3fd6-4a49-bfba-ff6850ad25b4
3385685|834ad22d-bb68-4644-9920-f53cc2ac6ae5|8bfcd5c0-7abf-40fb-9e0e-e109bb4d9668|35d80e23-ecbc-4614-aed6-e9aeaebd17aa|3e269410-bc43-474e-886d-0e79e2346e91
3385687|48d8d8b7-5a3c-4ba0-900d-6b72cbb00e00|87f39356-1f7a-40c1-80db-9dc2b4730c53|5f0d3a55-4842-4d86-a95d-077ae221f6bd|3e269410-bc43-474e-886d-0e79e2346e91
3385696|1206b04b-c197-4e9a-812f-4d915b7cc50d|bb917d6a-35c0-49a7-a93f-21203ef2c32c|13113e4b-5519-4a47-acda-d3eb1845db7c|84a83599-1145-42c5-b417-4cfc9ed0675f
3385697|275785b6-3fef-4832-a168-6eb418750e1f|2c98ea66-df81-4896-99d8-a5cf4d77d93f|e2ffa2e8-6fee-4384-80ac-2cc37aa6016d|fc84a0c5-0eeb-427a-a1c5-3562873c2d8e
3385698|d9f84437-1108-47b9-897e-5cceca87e89b|97c3698b-e206-4eba-bc4a-7cb20176831e|ae6c72c6-0f37-4659-bd28-acd71b2e89b9|fc84a0c5-0eeb-427a-a1c5-3562873c2d8e
3385822|9fc84479-183a-4de9-be94-69562208e2f8|f80b2566-d168-4ad9-90b0-092701ac5a72|10fd2955-ae21-4505-a25d-e5b98cb27829|9135d8dd-859f-4948-96ac-b21d09267c2b
3385824|04be9b9c-5b46-40d0-9c3a-a5394bccab86|2e008069-28c9-4495-bb23-cebeb6bc371a|51ce5ca8-c362-40e0-a754-baaa4cf8e1e6|9135d8dd-859f-4948-96ac-b21d09267c2b
3385832|de906261-fb59-4b6e-ae2b-18a165cfda63|1bd90681-88b3-47c5-abc8-55b96e3802fe|aa8561bf-02b2-4023-aacb-67ad3b8b49bf|639d3256-142d-49fc-84ce-39d8d009bab3
3385895|ac2f6ab5-d227-4bcd-a370-0effbf3784fc|e3c62106-6395-416a-9da7-6ee74985fb55|5d5e3979-cdd2-4da3-b6d0-9c74d39a791a|5dd8fa26-dd55-4c45-96ee-b65744a62fd2
3385896|a4fca4cc-93b8-474f-8b54-b2c1dbadac82|a191d84a-c5a0-4b64-a155-0ad63b8df8f1|a8f13168-26dc-4440-ae1b-a7dc8b697129|4603d121-7b1d-4da1-aa10-ac41b8588837
3385915|c3dcdb10-62bd-447a-b55a-2b5942672241|ab984217-26a3-4069-b239-3d1ddd225d70|6172cfd8-21be-48b6-bbb6-b3b07249344c|999b78bd-e113-4395-8299-cda42b8a5daf
3385935|1672fed8-d3fc-4762-a0b6-16a5bb0d86f6|9f26afc5-59af-479d-a550-792fe105670f|eb7ab0bb-0930-4525-88b5-ffd626edd990|d24c5a4b-96bf-451e-beb0-95888f350d7b
3385936|5b142d1b-e679-432e-9dbc-27da108f95a4|27b553b3-6725-4f18-bc68-d59a5dfd59d4|b51e57e6-60fd-45ae-be0d-187b95ee1d3a|6c86d992-f260-48cf-a196-90ba3cea978c
3385937|f2e3cb1b-7689-4388-b0d4-ac87fa36378c|40b9070a-70fd-4377-9481-aeb6c3873d68|69018b84-d509-4fac-96ef-68c6a15527f3|6c86d992-f260-48cf-a196-90ba3cea978c
3385938|7bfe2cf2-c01c-4c04-a848-a565ada3c11d|f53a1f50-b828-4825-9bd7-e1398250432d|b6d537aa-6124-442e-b03e-101991c05c9d|6c86d992-f260-48cf-a196-90ba3cea978c
3385941|1d1871f0-0f10-4c60-a5d2-e6626f0daa73|f61cb6c6-d84d-44ba-9581-b0f65b736f50|2a260cdb-e655-437b-934f-59f4ad3c9f19|ade5d717-4693-47cf-a330-6eaaaaac7c3f
3385943|ac2eb3d3-cf55-482d-9b69-647becc054fc|cdc302d0-ba3c-4a35-9f6b-7d5f8f88cc1a|a5edd24d-40b8-4274-b10c-238c4af1e98a|a795cf31-da28-486b-a95d-6ae079a49a51
3385944|ecda150c-2846-45bc-902e-8896f894d202|b0eefa72-bf33-45dc-86e4-affaba930b6d|9cd807b7-2947-4ffd-b76a-236cece39034|a795cf31-da28-486b-a95d-6ae079a49a51
3385945|17593d92-1c8a-424e-affc-35b5b6dc3dc2|b152c8b2-6365-4405-a6ad-43bba79e74b8|84501c2a-0ba9-47c9-860d-4dada620dc20|a795cf31-da28-486b-a95d-6ae079a49a51
3385946|dc71d693-3777-487e-8cc8-b4bad3752a81|89a1ae64-61bd-4574-a9b1-c93c7225466b|26321a5e-dc21-469c-831c-b4ade960f380|a795cf31-da28-486b-a95d-6ae079a49a51
3385956|23f6778d-55fc-47aa-a91f-6b58593b0a4d|41389d1a-7415-43cd-9f56-489c95695be5|bb7725bd-2908-4f32-b160-6760fadd9507|6d890450-0615-4952-973f-efe5e958c8e6
3385961|2a05e39e-588a-483b-8e91-a41d565ad82a|8978aff1-ffc4-43f2-b81c-9c2fa3deb3f6|66dc4667-3bee-417b-a4de-dbf58f1d7b2f|76b9bce7-b5ec-4433-9eab-2950df60bdfc
3385976|b450e5ee-434d-41d4-b0d0-848c5e06680b|45a12a49-8d16-4aec-816d-b10be67a43f2|35d2d1f9-b411-4bc9-8d94-c2ec21921283|b1b8f2c2-6fcd-47c4-b521-427b4fa7a9b8
3385977|3657383f-8abd-4c57-96be-a1e7ac8c939c|40fe9413-c284-4296-bf7e-42bf94bb21ae|9d397f3c-35d4-4ce3-9e41-f63a563ce0b0|b1b8f2c2-6fcd-47c4-b521-427b4fa7a9b8
3385992|53e2e147-730d-4499-8ace-5599b82482bd|7aaca9ec-5fdf-48cd-aabc-b98b5b459878|c1e5d3e4-3f75-4260-89e9-dcb7fdcd9e01|e75d2c43-0581-4090-bbf8-b2e8dd4cb562
3385993|fe3daa4b-28ac-41c8-834e-35e95a8e9331|da130f34-53a1-4818-8d1f-b2e4be4b729f|4a2b837b-4777-4151-9e4e-3dfe2c30ad87|e75d2c43-0581-4090-bbf8-b2e8dd4cb562
3385994|ed37682a-86ad-46c4-be34-55fa586c3fa7|633536fe-a484-4f2d-9ab6-bc6ca4b2f93c|faf61225-e6ec-40d0-a86f-8b15dd16b1cd|e75d2c43-0581-4090-bbf8-b2e8dd4cb562
3385995|59e933d7-e922-43bd-b88b-054c3dda10c4|e1642711-f564-4c35-934f-7fb2648605ae|5dd73b3e-d764-4ff8-80dc-4f6a2f607dc1|e75d2c43-0581-4090-bbf8-b2e8dd4cb562
3386002|039703a5-40ab-4091-a450-2bc87b41ae3f|1b87f89a-b540-4300-9ce2-21eeceecc76a|3ffcffd2-aa89-4e66-9c86-184bf28c37eb|6fc942ee-3b6d-41e4-a279-41b3686881d7
3386005|75073a9a-789a-4e76-8d22-e5c745890d3a|a947176f-baa0-47ea-8705-e00f7fb80fac|910788be-2b87-4553-bbf2-bf667871c9a9|249f29eb-b2d2-46c3-a8aa-666705dfa822
3388999|bab97159-686e-4a73-ad93-4b8ea31ea53e|41740cbf-75d5-4f0b-a79b-ad6320ad39d3|820df343-05f4-469d-bbd5-f3d1ddb1c966|2aa875a5-248c-4e9d-b889-f700f099b4d6
3389001|edaf8581-4265-48d9-9938-dc9d7a8ce160|db137a75-abd7-46ff-a09a-31e5af3d590d|7863d255-69bd-4c1a-ac1f-93c60d88aaf3|2aa875a5-248c-4e9d-b889-f700f099b4d6
3389011|9ca7340d-788c-4b54-9211-95bd62ce784b|76f2c3b1-21b2-4f7f-8cf5-225f760c8c13|195f93c4-0b30-4103-9cf2-40f164da35c7|ad57eeb3-b967-43aa-909b-b56fb86ffa46
3389012|b5dd95f7-fd8e-4a3d-ae29-be672866e389|365464a6-166a-4ee3-af36-8db8832ecbde|6ac528e3-7e27-4299-94dc-1016c3ea3cae|32f1dd09-99e6-4ac8-b5bd-d47e1d4c9477
3389024|bdb9c0bc-5c37-408a-8b16-6ef58e274fbf|30d4a8e4-aef1-4f2d-b6e4-4b41963c41fb|a6a87be8-13a4-49f8-9b59-2e04b0f49b7c|1e364f41-e568-4ca1-af04-e0291c236be5
3389026|1b886a47-50eb-49ca-8a10-3937887e4dd3|7e7e1b2e-6998-4a6f-802b-2b81c533dc64|71f8828a-22da-4ea6-a870-17549c840117|1e364f41-e568-4ca1-af04-e0291c236be5
3389084|21c55bea-9d3a-4c2a-bc56-fd8baf4fed8f|09e75fe5-978f-46e7-a2c8-6fb9088871bb|62115b71-45f1-4adf-b837-cbfedd03b7be|843eb89f-a38f-4515-81dc-d423e97a9704
3389092|cd3794b6-b75d-42d7-9b39-2717dc7d7463|6a984264-d3ec-42e0-aa49-185db2f3c373|2ab7c8a7-d647-4fea-bc11-30991c4968ec|66c2ee9a-90e6-4b86-b80a-35107f0cafa8
3389096|8417e79b-45fa-4c5f-a183-165a73edceba|101af956-fea6-44ec-83a4-d5cda4a734b3|dc998b6d-c3d5-4829-891f-1506087a557e|843eb89f-a38f-4515-81dc-d423e97a9704
3389100|98cca66d-be62-4a7d-9484-dd0e15f4fbe7|7afbfce4-9886-4751-9a18-0e0a691cc294|cbdbd0ff-1f76-4a67-b2ef-fd8cfafb2c8d|843eb89f-a38f-4515-81dc-d423e97a9704
3389108|98615089-107b-4ac6-a217-5fa40713167c|ee1b7b83-2913-47df-b535-ce3add7d0f91|eaea6e2b-e8d7-48b8-9ae0-304df03ec3e7|5694cb2f-ec8b-4392-a37d-9dd2f92317bf
3389111|ef80e1c3-b490-420a-a336-e6a827f91c83|86ba7d7b-65a3-42ec-b275-5d337c88510a|b3536e4c-3288-4d79-bf4b-e17678d73b34|a476bb82-214d-4550-b4c1-2c1a17a6c175
3389113|7f2bb9b9-ef6f-439f-949f-4a1e1836bc63|06d3e824-3ba7-4c57-9b8f-12c76015c854|4304fd65-3e34-43a6-ac6b-c0f6f7154baf|66c2ee9a-90e6-4b86-b80a-35107f0cafa8
3389115|2c671343-bf19-4209-8138-53e6d3311f9f|ad26ce70-7b9e-461b-8431-20275ad174e1|4ec9ef11-8ac0-4b28-8c4a-fa65bb0a1732|5aadc45e-cf86-4fd0-803b-7d01696b562a
3389383|8e0a2482-17bf-41f9-a187-d5a2b48d57f5|66d202d6-c8be-485b-a76e-1c46bee49484|285fed1c-1793-4a8f-a011-7069e9914c4a|244c473d-2f44-46df-9202-91fdf9577e9c
3389423|d05a4c76-250c-4feb-9665-4cf071ff9d40|345ae9b3-ec5f-46b3-86c7-03413e03a1d4|c8a5495f-469c-4d86-b4eb-982833184f55|d392cc55-b4b8-43e9-8b28-db934a30cd8f
3389858|cab17aa0-0780-4dda-9902-e346dbc6338b|b28c6210-4f79-4838-95de-a73f5fecccd9|8da6c55b-af0b-4ceb-8a88-bb62d9ed353f|6576473e-d752-4105-8f0f-372bc9bd0081
3389859|d1098761-edac-40d1-a731-1e4739d798f4|d0f6a28b-6d49-4ebb-8be3-7e600d242922|eafd88b5-a01a-4add-b3e8-5d209ad2d485|6576473e-d752-4105-8f0f-372bc9bd0081
3389866|8845420e-a32b-423d-94a8-69c71d821c1f|277288d7-4b48-4807-b53c-2130b3a50e3f|01df6247-15f6-4d24-b46a-ed2a6b4b429b|6576473e-d752-4105-8f0f-372bc9bd0081
3389868|71be06c7-f8f7-4278-b1db-0dac3917f9c3|44a31d46-f025-4782-9ce0-d4d779ea1da1|6ae30083-7794-4a45-a4ac-cde0b33b389d|3ba77f7c-ba92-4b13-9fb9-9b418bce7161
3389870|f6c51357-64d3-46b3-b68c-1f3e063da224|010876c7-3a8c-4fa7-8025-0934dd942cc3|51d710e7-c59d-44fa-accf-f18c7978bfbe|03b4fc44-dcdc-4b72-812b-4ea6fd00b7d8
3389872|2eacf05a-c0fb-47a1-b911-58d5ee240873|c9d79329-9c38-4de5-ac62-12fc8071f380|8cca0598-81a0-40c9-a3a9-1947b4ce7372|03b4fc44-dcdc-4b72-812b-4ea6fd00b7d8
3389876|63e370db-c24f-46d1-bb87-6cf7e690504e|cdfd4dab-6ff6-488d-88c0-062e1273deed|ad4698aa-b9ae-4648-b4ac-b944159da209|3ba77f7c-ba92-4b13-9fb9-9b418bce7161
3389877|966174df-02dc-4fa1-baa1-4f8abc2b2ea2|15b26fc4-bdbd-4477-9a00-893518d29493|6d76d60b-bb13-400a-9429-0066c44cb649|3ba77f7c-ba92-4b13-9fb9-9b418bce7161
3389878|f604e1f9-3823-45dc-85e5-27557ff2671f|7949574f-2245-491b-9247-506086fdee32|f1265534-7381-4fda-b35a-9d504af87f4d|3ba77f7c-ba92-4b13-9fb9-9b418bce7161
3389881|46962d7a-7a80-4530-b250-064cb10b80c6|c573d4d2-e264-4ded-8e6e-410cae5b120b|73aafe7e-da4a-4f69-bf02-66d7938df366|47ea7643-43cc-4b69-95e4-596b246b365b
3389883|f47ccbd5-6cf4-40c3-88b2-0f75dbf4fb7b|7011f91d-9d1a-41c9-922c-aab50163611b|c14996c4-fe53-4bad-8046-baf8c549d2a1|47ea7643-43cc-4b69-95e4-596b246b365b
3389884|97c496fb-9de6-433f-9943-573f030bd92e|9439efe1-6f13-4dde-b5a5-b1f069f20db2|ae9c385d-af35-4db9-b1fc-c6f7a27cf19b|47ea7643-43cc-4b69-95e4-596b246b365b
3389886|3aaaa9a9-862d-42d1-ab40-1f761dff4d44|43184f5f-095c-41ee-bd10-8cb82223da1a|724dac23-785b-4c39-bb44-2a69d6c1eb6e|3ba77f7c-ba92-4b13-9fb9-9b418bce7161
3389887|4883907b-07f9-4c99-8648-b04d8b33c75b|d1534791-c197-4b12-afef-4533a662c555|19c0e45d-9afa-4187-80c8-7d672f9d4db3|3ba77f7c-ba92-4b13-9fb9-9b418bce7161
3389892|2cc905bb-94ad-47ac-89b2-3190cdb41cf4|c3b527fc-9957-4ecc-8217-56f5b2cc0f31|dee68cb8-3c4e-4316-8be6-db63edb7dec8|3ba77f7c-ba92-4b13-9fb9-9b418bce7161
3389926|d9f5b986-fc8c-4ab8-8563-4278f239ff3a|b89f33a9-680b-4b3c-9d23-5309e0e5b1b5|6ae9835a-6214-401e-b8ef-f1c0377b4440|3ba77f7c-ba92-4b13-9fb9-9b418bce7161
3390104|afad9d8a-a397-4a72-ae8a-f4e626a94990|bebdfdaa-4e1f-4ff7-b010-ed1af5747627|a1e579b6-1176-4b77-851e-648bba717ba8|12e5d03d-907d-4344-8aa2-c2789b4630e5
3390105|db67cfd3-919c-4d65-8339-f2d373c4648c|4606a4a7-0f8f-4860-9982-063468e674fe|2e5922a3-6fa5-4b70-96c5-c25542238af9|0682b20c-e5a3-47c1-82c8-572cab92592a
3390106|119c0376-9a9a-4292-bed9-422a448bba70|105fb6b1-1e6a-4e4b-a6e7-6a8f3b91bafc|859f4456-8599-426a-8bdb-4cb577aac2fd|0682b20c-e5a3-47c1-82c8-572cab92592a
3390107|9f57844e-60c4-4743-ad88-39e4c9457cf5|a14ee1a9-13bb-4853-8de7-aec481e01e0e|cfcfeb2a-ade9-4a4c-b064-8cf4fd8cd457|0682b20c-e5a3-47c1-82c8-572cab92592a
3390108|fc0cf9c9-83fb-4f31-b99c-b5c98bf89965|39455d74-d3c7-437e-858b-df76cdf3b45c|66b5b0d9-4f25-4f6f-b886-1d2b35f419d0|90fd380d-8705-4c17-8cca-6074819beba6
3390109|1919d4e5-698d-479a-8eb2-c7d7472563fa|443bf3dd-aea4-4d59-95ca-60581486e137|34fa0144-452d-416b-a978-fef2e0fad0c0|90fd380d-8705-4c17-8cca-6074819beba6
3390110|1d3cbed3-c23c-48a8-a47a-35eb2c3e5c75|4bb39aec-0f12-4e13-ae92-9745874edd48|f5b133a7-5473-4749-9353-c9fb4c3221fa|8cdacf53-a4e3-4f28-8cf4-083512261443
3390111|f6f34c2e-dd4d-49a2-86d3-c82fa52240b2|f8521c13-8da3-4edb-b777-feddbe656785|42b2daf1-45cb-4b00-9315-bcccc7931d1f|8cdacf53-a4e3-4f28-8cf4-083512261443
3390112|7a852ce0-75eb-4a7d-86f3-278188844757|8d6e7a48-bf97-4333-9543-405d259e124d|8ac545a4-932d-44e8-b8a3-20e8e5c94c13|8dd8b1bd-99e5-4733-b8b8-42a677afcfbb
3390115|3a307418-cfb9-4225-91f4-3c524c13ee4b|2a049884-1df5-4593-b9d7-e2c38528e336|bfc93384-3a95-4cd0-bc93-71f18d9a4f54|0262017c-1ca2-4d3e-a214-256e11fd6ccd
3390119|b82ee50b-a1d3-4f43-8897-413a65143f56|9311ceda-b303-4d4a-b71e-9c344ea09cdd|ef22ded9-2b67-4fa1-8daf-67d73ec19029|ffa614ff-a447-4bd1-8f5e-c09e98ca3829
3390120|15392f5f-83e8-4f41-b95d-38d7572dbf79|df136a79-6f30-4ad4-be3f-20f9999a1ab7|35f6d788-8884-4803-a46a-c0ba3235cb44|ffa614ff-a447-4bd1-8f5e-c09e98ca3829
3390127|8efc814f-7224-4385-996f-3fa0de479a72|df3b9572-b3b2-4bf6-9772-d360d08ab905|fdccb473-3054-4d26-b312-23e5e05f3635|f06f9be1-1ba3-4d13-85bf-75b9af985b61
3390128|5e61afe4-250b-4e1b-af21-f9f22b3d09d2|ff97aac4-420d-4da7-bbd2-e70ef1d718f9|87b30e99-df4e-4450-9bcc-a8c39f47c0de|f06f9be1-1ba3-4d13-85bf-75b9af985b61
3390129|c2c07d88-2832-40a8-840d-77e9b888a2b3|07e12828-a665-4867-9ff6-bcbe3302c91d|b418d1df-18e8-49c7-9456-a7295208959e|f06f9be1-1ba3-4d13-85bf-75b9af985b61
3390131|b88bbebe-4c98-4ea3-97f1-99442ccf6756|22e80695-4c43-49bb-90df-572ffe0a81c6|869cc655-97c4-4c9e-97e6-da0b074ad588|f06f9be1-1ba3-4d13-85bf-75b9af985b61
3390132|e22e7dbf-bfaa-4fa9-9ed8-b59100855571|57f57a67-56e3-4f7e-97eb-3c78c4c55704|b7a655d3-45f4-4ccd-84c2-abb724e590bb|f06f9be1-1ba3-4d13-85bf-75b9af985b61
3390133|c7066b9e-5a59-4b40-9012-0bd7bf82bf3c|4f98e700-9ec9-4406-a418-8cc72dcd66d0|12b1646b-3306-4022-ac40-f873cc6d82b0|bf39da0b-2b78-4f8a-9a45-474cf051dcd2
3390134|6cbc4b74-884e-42b1-9709-b7b65c8c6958|aab37ea2-a092-4552-9610-0b457d514b78|774d31e2-76bf-4552-a973-25fcac58f601|bf39da0b-2b78-4f8a-9a45-474cf051dcd2
3390135|cf783823-73d5-4826-a248-822aeccac98e|2a7c9670-8dd3-46c5-811c-cd96663aef7e|fca507c5-ec21-4fe3-baac-466e922191f1|bf39da0b-2b78-4f8a-9a45-474cf051dcd2
@@ -2,7 +2,11 @@ package medicationdispense
import (
"context"
"database/sql"
"fmt"
"service/internal/infrastructure/database"
"service/pkg/logger"
"service/pkg/utils/query"
)
@@ -27,28 +31,125 @@ func (r *repository) GetNextMedicationDispense(ctx context.Context, lastID int64
return nil, err
}
qb := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).SetSecurityOptions(false, 0)
qb := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).
SetSecurityOptions(false, 0).
SetQueryLogging(false).
SetAllowedColumns([]string{"idxpesanobat", `"IDXDAFTAR"`, `"KDDOKTER"`})
type simrsRow struct {
IdxPesanObat int64 `db:"idxpesanobat"`
NoJual sql.NullString `db:"no_jual"`
IdxDaftar int64 `db:"idxdaftar"`
KdDokter sql.NullString `db:"kddokter"`
JmlhKeluar sql.NullFloat64 `db:"jmlh_keluar"`
Sediaan sql.NullString `db:"sediaan"`
CreatedAd sql.NullTime `db:"created_at"`
TglPesan sql.NullTime `db:"tgl_pesan"`
TglKeluar sql.NullTime `db:"tgl_keluar"`
}
var row simrsRow
// TODO: Sesuaikan nama tabel pemberian obat (Apotek)
qSimrs := query.DynamicQuery{
From: "public.t_pemberian_obat",
From: "public.t_permintaan_apotek_rajal",
Fields: []query.SelectField{
{Expression: "idxpesanobat"},
{Expression: "no_jual"},
{Expression: "idxdaftar"},
{Expression: "kddokter"},
{Expression: "jmlh_keluar"},
{Expression: "sediaan"},
{Expression: "created_at"},
{Expression: "tgl_pesan"},
{Expression: "tgl_keluar"},
},
Filters: []query.FilterGroup{
{
Filters: []query.DynamicFilter{
query.CreateFilter("idxpemberian", query.OpGreaterThan, lastID),
query.CreateFilter("idxpesanobat", query.OpGreaterThan, lastID),
},
},
},
Sort: []query.SortField{
query.CreateAscSort("idxpemberian"),
query.CreateAscSort("idxpesanobat"),
},
Limit: 1,
}
err = qb.ExecuteQueryRow(ctx, simrsDB, qSimrs, &md)
err = qb.ExecuteQueryRow(ctx, simrsDB, qSimrs, &row)
if err != nil {
return nil, err
}
md.IdxPesanObat = row.IdxPesanObat
md.NoJual = row.NoJual.String
md.IdxDaftar = row.IdxDaftar
md.JmlhKeluar = row.JmlhKeluar.Float64
md.Sediaan = row.Sediaan.String
if row.CreatedAd.Valid {
md.PreparedDate = row.CreatedAd.Time
} else if row.TglPesan.Valid {
md.PreparedDate = row.TglPesan.Time
} else {
md.PreparedDate = logger.LocalNow()
}
if row.CreatedAd.Valid {
md.HandedOverDate = row.CreatedAd.Time
} else if row.TglKeluar.Valid {
md.HandedOverDate = row.TglKeluar.Time
} else if row.TglPesan.Valid {
md.HandedOverDate = row.TglPesan.Time
} else {
md.HandedOverDate = logger.LocalNow()
}
satudataDB, err := r.dbManager.GetSQLXDB("satudata")
if err != nil {
return nil, fmt.Errorf("gagal koneksi ke database satudata: %w", err)
}
if md.IdxDaftar > 0 {
type kunjunganRow struct {
PatientID sql.NullString `db:"Nomor_satusehat_pasien"`
EncounterID sql.NullString `db:"IDXDAFTAR_satusehat"`
}
var kRow kunjunganRow
qKunjungan := query.DynamicQuery{
From: "public.data_kunjungan_pasien",
Fields: []query.SelectField{
{Expression: `"Nomor_satusehat_pasien"`},
{Expression: `"IDXDAFTAR_satusehat"`},
},
Filters: []query.FilterGroup{
{Filters: []query.DynamicFilter{query.CreateEqualFilter(`"IDXDAFTAR"`, md.IdxDaftar)}},
},
Limit: 1,
}
qb.ExecuteQueryRow(ctx, satudataDB, qKunjungan, &kRow)
md.PatientID = kRow.PatientID.String
md.EncounterID = kRow.EncounterID.String
}
if row.KdDokter.Valid && row.KdDokter.String != "" {
type pegawaiRow struct {
PractitionerID sql.NullString `db:"Kode_satusehat"`
}
var pRow pegawaiRow
qPegawai := query.DynamicQuery{
From: "public.data_pegawai",
Fields: []query.SelectField{
{Expression: `"Kode_satusehat"`},
},
Filters: []query.FilterGroup{
{Filters: []query.DynamicFilter{query.CreateEqualFilter(`"KDDOKTER"`, row.KdDokter.String)}},
},
Limit: 1,
}
qb.ExecuteQueryRow(ctx, satudataDB, qPegawai, &pRow)
md.PractitionerID = pRow.PractitionerID.String
}
return &md, nil
}
@@ -57,8 +158,11 @@ func (r *repository) SaveSatuSehatID(ctx context.Context, idx int64, fhirID stri
if err != nil {
return err
}
qb := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).SetSecurityOptions(false, 0)
_, err = qb.ExecuteUpdate(ctx, simrsDB, "public.t_pemberian_obat", query.UpdateData{Columns: []string{"status_bridging"}, Values: []interface{}{fhirID}}, []query.FilterGroup{query.CreateAndFilterGroup([]query.DynamicFilter{query.CreateEqualFilter("idxpemberian", idx)})})
qb := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).
SetSecurityOptions(false, 0).
SetQueryLogging(false).
SetAllowedColumns([]string{"status_bridging_dispense", "idxpesanobat"})
_, err = qb.ExecuteUpdate(ctx, simrsDB, "public.t_permintaan_apotek_rajal", query.UpdateData{Columns: []string{"status_bridging_dispense"}, Values: []interface{}{fhirID}}, []query.FilterGroup{query.CreateAndFilterGroup([]query.DynamicFilter{query.CreateEqualFilter("idxpesanobat", idx)})})
return err
}
@@ -67,11 +171,14 @@ func (r *repository) SaveSyncLog(ctx context.Context, logData MedicationDispense
if err != nil {
return err
}
qb := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).SetSecurityOptions(false, 0)
qb := query.NewSQLQueryBuilder(query.DBTypePostgreSQL).
SetSecurityOptions(false, 0).
SetQueryLogging(false).
SetAllowedColumns([]string{"idxpesanobat", "medicationdispense_id", "request_payload", "response_payload", "status", "error_message"})
insertData := query.InsertData{
Columns: []string{"idxpemberian", "medicationdispense_id", "request_payload", "response_payload", "status", "error_message"},
Values: []interface{}{logData.IdxPemberian, logData.MedicationDispenseID, logData.RequestPayload, logData.ResponsePayload, logData.Status, logData.ErrorMessage},
Columns: []string{"idxpesanobat", "medicationdispense_id", "request_payload", "response_payload", "status", "error_message"},
Values: []interface{}{logData.IdxPesanObat, logData.MedicationDispenseID, logData.RequestPayload, logData.ResponsePayload, logData.Status, logData.ErrorMessage},
}
_, err = qb.ExecuteUpsert(ctx, simrsDB, "public.log_satusehat_medicationdispense", insertData, []string{"idxpemberian"}, []string{"medicationdispense_id", "request_payload", "response_payload", "status", "error_message"})
_, err = qb.ExecuteUpsert(ctx, simrsDB, "public.log_satusehat_medicationdispense", insertData, []string{"idxpesanobat"}, []string{"medicationdispense_id", "request_payload", "response_payload", "status", "error_message"})
return err
}
@@ -1,68 +1,416 @@
package medicationdispense
import (
"bufio"
"context"
"encoding/json"
"fmt"
"log"
"io/fs"
"net/http"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
"service/internal/infrastructure/database"
extapi "service/internal/worker/interface"
"service/pkg/logger"
)
const trackerFile = "last_migrated_medicationdispense_id.txt"
const (
trackerFile = "internal/worker/satusehat/medicationdispense/last_medicationdispense_id.txt"
medReqMappingFile = "internal/worker/satusehat/medicationrequest/medicationrequest_mapping.txt"
medReqMappingBaseDir = "internal/worker/satusehat/medicationrequest/mapperdata"
medMappingFile = "internal/worker/satusehat/medication/medication_mapping.txt"
medMappingBaseDir = "internal/worker/satusehat/medication/mapperdata"
medDispenseMappingBaseDir = "internal/worker/satusehat/medicationdispense/mapperdata"
rateLimitSleep = 60 * time.Second
)
type Config struct {
DBManager database.Service
InternalBaseURL string
InternalToken string
OrganizationID string
}
type TokenManager interface {
GetAccessToken() string
ForceRefreshAndGetToken(ctx context.Context) (string, error)
}
type WorkerService interface {
Run(ctx context.Context)
}
type worker struct {
cfg Config
repo Repository
apiClient extapi.Client
type mappedData struct {
MedicationRequestID string
MedicationID string
EncounterID string
}
func NewWorker(cfg Config) WorkerService {
return &worker{cfg: cfg, repo: NewRepository(cfg.DBManager), apiClient: extapi.NewClient(15 * time.Second)}
type mappingFileInfo struct {
Path string
Count int
MinID int64
MaxID int64
}
type worker struct {
cfg Config
repo Repository
apiClient extapi.Client
tokenManager TokenManager
medReqMap map[int64]mappedData
medFhirMap map[int64]string // fallback medicationID dari mapping medication (untuk entry legacy 2-part)
minMedReqID int64
maxMedReqID int64
fileStats []mappingFileInfo // statistik per-file mapping medicationrequest (urut kronologis)
}
func NewWorker(cfg Config, tokenManager TokenManager) WorkerService {
w := &worker{
cfg: cfg,
repo: NewRepository(cfg.DBManager),
apiClient: extapi.NewClient(15 * time.Second),
tokenManager: tokenManager,
medReqMap: make(map[int64]mappedData),
medFhirMap: make(map[int64]string),
}
w.loadMedicationRequestMapping()
w.loadMedicationFhirMapping()
return w
}
func (w *worker) loadMedicationRequestMapping() {
var minID, maxID int64
minSet := false
var stats []mappingFileInfo
loadFromFile := func(path string) {
file, err := os.Open(path)
if err != nil {
return
}
defer file.Close()
info := mappingFileInfo{Path: path}
fileMinSet := false
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
parts := strings.Split(line, "|")
// Format yang didukung:
// 2 parts (legacy): id|medicationRequestFhirID
// 3 parts : id|medicationRequestFhirID|medicationFhirID
// 4 parts : id|medicationRequestFhirID|medicationFhirID|encounterID
// Entry 2-part legacy tidak memiliki medicationID/encounterID — keduanya
// di-fallback dari mapping medication & data DB pada saat runtime.
if len(parts) >= 2 {
id, err := strconv.ParseInt(parts[0], 10, 64)
if err == nil {
mData := mappedData{
MedicationRequestID: parts[1],
}
if len(parts) >= 3 {
mData.MedicationID = parts[2]
}
if len(parts) >= 4 {
mData.EncounterID = parts[3]
}
// Jika sudah ada entry sebelumnya yang lebih lengkap, jangan timpa
// dengan entry yang lebih ringkas (mis. legacy 2-part menimpa daily 4-part).
if existing, ok := w.medReqMap[id]; ok {
if mData.MedicationID == "" {
mData.MedicationID = existing.MedicationID
}
if mData.EncounterID == "" {
mData.EncounterID = existing.EncounterID
}
}
w.medReqMap[id] = mData
info.Count++
if !fileMinSet || id < info.MinID {
info.MinID = id
fileMinSet = true
}
if id > info.MaxID {
info.MaxID = id
}
if !minSet || id < minID {
minID = id
minSet = true
}
if id > maxID {
maxID = id
}
}
}
}
if info.Count > 0 {
stats = append(stats, info)
}
}
// Kumpulkan path mapping dalam urutan kronologis: legacy (paling tua) → file harian
// (mapperdata/<YYYY-MM>/<YYYY-MM-DD>.txt) di-sort ascending sehingga tanggal terkecil
// dimuat lebih dulu. Worker akan menghabiskan ID tanggal terkecil sebelum lanjut ke
// tanggal berikutnya (urutan iterasi tetap ditentukan oleh `lastID` dari DB query).
var paths []string
if _, err := os.Stat(medReqMappingFile); err == nil {
paths = append(paths, medReqMappingFile)
}
if _, err := os.Stat(medReqMappingBaseDir); err == nil {
var dailyPaths []string
errWalk := filepath.WalkDir(medReqMappingBaseDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() || !strings.HasSuffix(d.Name(), ".txt") {
return nil
}
dailyPaths = append(dailyPaths, path)
return nil
})
if errWalk != nil {
logger.Default().Warn("[MEDICATIONDISPENSE WORKER] Gagal walk direktori mapping medicationrequest", logger.ErrorField(errWalk))
}
sort.Strings(dailyPaths)
paths = append(paths, dailyPaths...)
}
for _, p := range paths {
loadFromFile(p)
}
w.minMedReqID = minID
w.maxMedReqID = maxID
w.fileStats = stats
}
// logMappingProgress menampilkan ringkasan per-file mapping medicationrequest
// pada saat startup, menunjukkan apakah data tanggal-tanggal sebelumnya sudah
// DONE, IN_PROGRESS, atau masih PENDING relatif terhadap lastID. Tujuannya:
// memastikan tidak ada hari yang terlewat sebelum lanjut ke hari berikutnya.
func (w *worker) logMappingProgress(lastID int64) {
if len(w.fileStats) == 0 {
logger.Default().Warn("[MEDICATIONDISPENSE WORKER] ⚠️ Tidak ada file mapping medicationrequest yang dimuat")
return
}
logger.Default().Info("[MEDICATIONDISPENSE WORKER] 📋 Status file mapping medicationrequest (urut kronologis)",
logger.Int("file_count", len(w.fileStats)),
logger.Int64("last_id", lastID),
)
for _, fs := range w.fileStats {
var status string
switch {
case fs.MaxID <= lastID:
status = "DONE"
case fs.MinID > lastID:
status = "PENDING"
default:
status = "IN_PROGRESS"
}
logger.Default().Info("[MEDICATIONDISPENSE WORKER] 📁 "+filepath.Base(fs.Path),
logger.String("status", status),
logger.Int("count", fs.Count),
logger.Int64("min_id", fs.MinID),
logger.Int64("max_id", fs.MaxID),
)
}
}
// loadMedicationFhirMapping memuat seluruh mapping medication (idxPesanObat → medicationFhirID)
// untuk fallback medicationID pada entry legacy medicationrequest yang hanya 2 parts.
func (w *worker) loadMedicationFhirMapping() {
loadFromFile := func(path string) {
file, err := os.Open(path)
if err != nil {
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
parts := strings.Split(line, "|")
if len(parts) >= 2 {
id, err := strconv.ParseInt(parts[0], 10, 64)
if err == nil && parts[1] != "" {
w.medFhirMap[id] = parts[1]
}
}
}
}
var paths []string
if _, err := os.Stat(medMappingFile); err == nil {
paths = append(paths, medMappingFile)
}
if _, err := os.Stat(medMappingBaseDir); err == nil {
var dailyPaths []string
errWalk := filepath.WalkDir(medMappingBaseDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() || !strings.HasSuffix(d.Name(), ".txt") {
return nil
}
dailyPaths = append(dailyPaths, path)
return nil
})
if errWalk != nil {
logger.Default().Warn("[MEDICATIONDISPENSE WORKER] Gagal walk direktori mapping medication", logger.ErrorField(errWalk))
}
sort.Strings(dailyPaths)
paths = append(paths, dailyPaths...)
}
for _, p := range paths {
loadFromFile(p)
}
}
func (w *worker) appendMedicationDispenseMapping(idx int64, fhirID, medicationRequestID, medicationID, encounterID string) {
now := logger.LocalNow()
monthDir := filepath.Join(medDispenseMappingBaseDir, now.Format("2006-01"))
if err := os.MkdirAll(monthDir, 0755); err != nil {
logger.Default().Error("[MEDICATIONDISPENSE WORKER] Gagal membuat direktori mapping bulanan", logger.ErrorField(err))
return
}
mappingPath := filepath.Join(monthDir, now.Format("2006-01-02")+".txt")
f, err := os.OpenFile(mappingPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
logger.Default().Error("[MEDICATIONDISPENSE WORKER] Gagal membuka/membuat file mapping medicationdispense", logger.ErrorField(err))
return
}
defer f.Close()
_, _ = f.WriteString(fmt.Sprintf("%d|%s|%s|%s|%s\n", idx, fhirID, medicationRequestID, medicationID, encounterID))
}
func (w *worker) Run(ctx context.Context) {
log.Println("[MEDICATIONDISPENSE WORKER] Memulai proses sinkronisasi...")
lastID := w.readLastID()
// Jika lastID 0, mulai dari ID terkecil pada tanggal terkecil pada mapping
// medicationrequest (file dimuat kronologis: legacy → harian asc), sehingga semua
// data tanggal terkecil dihabiskan dulu sebelum berlanjut ke tanggal berikutnya.
if lastID == 0 && w.minMedReqID > 0 {
lastID = w.minMedReqID - 1
}
logger.Default().Info("[MEDICATIONDISPENSE WORKER] ⏳ Memulai proses sinkronisasi...", logger.Int64("lastID", lastID))
w.logMappingProgress(lastID)
// testCount := 0
for {
select {
case <-ctx.Done():
log.Println("[MEDICATIONDISPENSE WORKER] Proses dihentikan.")
logger.Default().Info("[MEDICATIONDISPENSE WORKER] 🛑 Proses dihentikan.")
return
default:
}
md, err := w.repo.GetNextMedicationDispense(ctx, lastID)
if err != nil {
if !strings.Contains(err.Error(), "no rows") {
logger.Default().Error("[MEDICATIONDISPENSE WORKER] ❌ Gagal query data dari DB", logger.ErrorField(err))
}
time.Sleep(5 * time.Second)
continue
}
// Cari MedicationRequest ID dan Medication ID dari mapping internal memory
mData, ok := w.medReqMap[md.IdxPesanObat]
if !ok {
// Coba reload mapping file barangkali ada data baru
w.loadMedicationRequestMapping()
mData, ok = w.medReqMap[md.IdxPesanObat]
}
if ok {
md.MedicationRequestID = mData.MedicationRequestID
md.MedicationID = mData.MedicationID
if mData.EncounterID != "" && md.EncounterID == "" {
md.EncounterID = mData.EncounterID
}
// Fallback: medicationID dari mapping medication untuk entry legacy 2-part
// yang tidak menyertakan medicationFhirID di file medicationrequest_mapping.
if md.MedicationID == "" {
medID, found := w.medFhirMap[md.IdxPesanObat]
if !found {
w.loadMedicationFhirMapping()
medID, found = w.medFhirMap[md.IdxPesanObat]
}
if found {
md.MedicationID = medID
}
}
} else {
if md.IdxPesanObat <= w.maxMedReqID {
logger.Default().Warn(fmt.Sprintf("[MEDICATIONDISPENSE WORKER] ⏭️ Skip Dokumen: MedicationRequest ID tidak ditemukan di mapping untuk IdxPesanObat: %d (Di-skip/Error di MedicationRequest Worker)", md.IdxPesanObat))
lastID = md.IdxPesanObat
os.MkdirAll("internal/worker/satusehat/medicationdispense", 0755)
os.WriteFile(trackerFile, []byte(strconv.FormatInt(lastID, 10)), 0644)
} else {
logger.Default().Info(fmt.Sprintf("[MEDICATIONDISPENSE WORKER] ⏳ Menunggu data mapping baru dari MedicationRequest Worker untuk IdxPesanObat: %d...", md.IdxPesanObat))
time.Sleep(5 * time.Second)
}
continue
}
// Validasi data mandatory dari satudata
if md.PatientID == "" || md.EncounterID == "" || md.PractitionerID == "" || md.MedicationRequestID == "" || md.MedicationID == "" {
logger.Default().Warn("[MEDICATIONDISPENSE WORKER] ⏭️ Skip Dokumen: Data mandatory (PatientID/EncounterID/PractitionerID/MedicationRequestID/MedicationID) kosong",
logger.Int64("idxpesanobat", md.IdxPesanObat),
logger.String("patient_id", md.PatientID),
logger.String("encounter_id", md.EncounterID),
logger.String("practitioner_id", md.PractitionerID),
logger.String("medication_request_id", md.MedicationRequestID),
)
lastID = md.IdxPesanObat
os.MkdirAll("internal/worker/satusehat/medicationdispense", 0755)
os.WriteFile(trackerFile, []byte(strconv.FormatInt(lastID, 10)), 0644)
continue
}
internalPayload := MapToInternalAPI(md, w.cfg.OrganizationID)
reqURL := fmt.Sprintf("%s/satusehat/medicationdispense", strings.TrimRight(w.cfg.InternalBaseURL, "/"))
respBody, statusCode, err := w.apiClient.PostJSON(ctx, reqURL, w.cfg.InternalToken, internalPayload)
logger.Default().Info("[MEDICATIONDISPENSE WORKER] 🚀 Mengirim request ke API", logger.Int64("idxpesanobat", md.IdxPesanObat), logger.Any("payload", internalPayload))
respBody, statusCode, err := w.apiClient.PostJSON(ctx, reqURL, w.tokenManager.GetAccessToken(), internalPayload)
if statusCode == http.StatusUnauthorized {
logger.Default().Warn("[MEDICATIONDISPENSE WORKER] ⚠️ Token expired, refreshing...")
if newToken, errRef := w.tokenManager.ForceRefreshAndGetToken(ctx); errRef == nil {
respBody, statusCode, err = w.apiClient.PostJSON(ctx, reqURL, newToken, internalPayload)
}
}
if isRateLimitResponse(statusCode, respBody) {
logger.Default().Warn("[MEDICATIONDISPENSE WORKER] ⏸️ Rate limit dari Satu Sehat - jeda dan akan retry dokumen yang sama",
logger.Int64("idxpesanobat", md.IdxPesanObat),
logger.Int("status_code", statusCode),
logger.String("response", truncateMD(string(respBody), 300)),
logger.String("sleep", rateLimitSleep.String()),
)
time.Sleep(rateLimitSleep)
continue
}
var status, errMsg, fhirID string
if err != nil || (statusCode != http.StatusOK && statusCode != http.StatusCreated) {
status, errMsg = "FAILED", fmt.Sprintf("Err: %v, HTTP: %d", err, statusCode)
logger.Default().Error("[MEDICATIONDISPENSE WORKER] ❌ Gagal mengirim data", logger.String("error", errMsg), logger.String("response", string(respBody)))
} else {
status = "SUCCESS"
var res map[string]interface{}
@@ -73,21 +421,54 @@ func (w *worker) Run(ctx context.Context) {
fhirID, _ = res["id"].(string)
}
}
logger.Default().Info("[MEDICATIONDISPENSE WORKER] ✅ Berhasil mengirim data", logger.String("fhir_id", fhirID), logger.String("response", string(respBody)))
}
_ = w.repo.SaveSyncLog(ctx, MapToSyncLog(md.IdxPemberian, fhirID, internalPayload, respBody, status, errMsg))
_ = w.repo.SaveSyncLog(ctx, MapToSyncLog(md.IdxPesanObat, fhirID, internalPayload, respBody, status, errMsg))
if status == "SUCCESS" && fhirID != "" {
_ = w.repo.SaveSatuSehatID(ctx, md.IdxPemberian, fhirID)
_ = w.repo.SaveSatuSehatID(ctx, md.IdxPesanObat, fhirID)
w.appendMedicationDispenseMapping(md.IdxPesanObat, fhirID, md.MedicationRequestID, md.MedicationID, md.EncounterID)
}
lastID = md.IdxPemberian
lastID = md.IdxPesanObat
os.MkdirAll("internal/worker/satusehat/medicationdispense", 0755)
os.WriteFile(trackerFile, []byte(strconv.FormatInt(lastID, 10)), 0644)
time.Sleep(500 * time.Millisecond)
// testCount++
// if testCount >= 5 {
// logger.Default().Info("[MEDICATIONDISPENSE WORKER] 🛑 Uji coba 5 data selesai. Menghentikan worker.")
// return
// }
}
}
func (w *worker) readLastID() int64 {
data, _ := os.ReadFile(trackerFile)
data, err := os.ReadFile(trackerFile)
if err != nil {
return 0
}
id, _ := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64)
return id
}
// isRateLimitResponse mendeteksi respons rate-limit dari Satu Sehat
// (HTTP 429 atau pesan body yang mengindikasikan limit pengiriman terlampaui).
func isRateLimitResponse(statusCode int, respBody []byte) bool {
if statusCode == http.StatusTooManyRequests {
return true
}
body := strings.ToLower(string(respBody))
return strings.Contains(body, "too many requests") ||
strings.Contains(body, "rate limit") ||
strings.Contains(body, "limit exceeded") ||
strings.Contains(body, "limit pengiriman") ||
strings.Contains(body, "quota exceeded")
}
func truncateMD(s string, max int) string {
if len(s) > max {
return s[:max] + "..."
}
return s
}