Advertisement
DWC-cristo

Tokko - Sheets

Mar 27th, 2025
17
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.96 KB | Source Code | 0 0
  1. function obtenerPropiedades() {
  2. const apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxx"; // Reemplazar con tu token
  3. const baseUrl = "https://www.tokkobroker.com/api/v1/property/?key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&format=json&lang=es_ar";
  4. const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  5. const totalProperties = 500;
  6. const limit = 50;
  7.  
  8. // Mapeo de traducciones al español para encabezados
  9. const headerTranslations = {
  10. "address": "Dirección1",
  11. "age": "Antigüedad",
  12. "bathroom_amount": "Cantidad de baños",
  13. "depth_measure": "Medida de profundidad",
  14. "description": "Descripción",
  15. "development": "Emprendimiento / Inversion",
  16. "disposition": "Disposición",
  17. "expenses": "Expensas",
  18. "floors_amount": "Cantidad de pisos",
  19. "front_measure": "Medida del frente",
  20. "id": "ID",
  21. "location": "Zona / barrio / localidad",
  22. "operation_currency": "Divisa",
  23. "operation_price": "Precio",
  24. "operation_type": "Tipo de operación",
  25. "parking_lot_amount": "Cantidad de cocheras",
  26. "producer": "Productor",
  27. "property_condition": "Estado de la propiedad",
  28. "public_url": "URL pública",
  29. "publication_title": "Título",
  30. "reference_code": "Código de referencia",
  31. "roofed_surface": "Superficie cubierta",
  32. "room_amount": "Ambientes",
  33. "semiroofed_surface": "Superficie semicubierta",
  34. "situation": "Situación",
  35. "suite_amount": "Cantidad de habitaciones",
  36. "surface": "Superficie",
  37. "surface_measurement": "Medida de superficie",
  38. "tags": "Caracteristicas",
  39. "toilet_amount": "Cantidad de toilettes",
  40. "total_surface": "Superficie total",
  41. "transaction_requirements": "Requisitos de transacción",
  42. "type": "Tipo de propiedad",
  43. "unroofed_surface": "Superficie descubierta",
  44. "videos": "Videos",
  45. "zonification": "Zonificación",
  46. "development_excel_extra_data": "Datos extra de desarrollo",
  47. "extra_attributes": "Caracteristicas extra",
  48. "fake_address": "Dirección",
  49. "has_temporary_rent": "Tiene alquiler temporal"
  50. };
  51.  
  52. // Mapeo de traducción de tipos de propiedad
  53. const typeTranslations = {
  54. "Apartment": "Departamento",
  55. "Bussiness Permit": "Permiso de negocio",
  56. "Bussiness Premises": "Local comercial",
  57. "Condo": "Condominio",
  58. "Countryside": "Campo",
  59. "Garage": "Garaje",
  60. "Hotel": "Hotel",
  61. "House": "Casa",
  62. "Land": "Terreno",
  63. "Office": "Oficina",
  64. "Warehouse": "Deposito",
  65. "Weekend House": "Casa de fin de semana"
  66. };
  67.  
  68. // Mapeo de traducciones al español para valores de tags
  69. const translations = {
  70. "Electricity": "Electricidad",
  71. "Heater": "Calefacción",
  72. "Service entrance": "Entrada de servicio",
  73. "Moorings": "Muelle",
  74. "Service Lift": "Ascensor de servicio",
  75. "Refrigeration by forced air cooling": "Refrigeración por aire forzado",
  76. "Basquet Field": "Campo de basquet",
  77. "Balanced draft heating": "Calefacción por tiro balanceado",
  78. "Linens": "Ropa de cama",
  79. "PVC enclosures": "Cierres de PVC",
  80. "Library": "Biblioteca",
  81. "Scottish shower": "Ducha escocesa",
  82. "Aluminium Carpentry": "Carpintería de aluminio",
  83. "Individual boiler": "Caldera individual",
  84. "Parking space": "Espacio de estacionamiento",
  85. "Equestrian": "Ecuestre",
  86. "central air conditioning": "Aire acondicionado central",
  87. "Central water heater": "Calentador de agua central",
  88. "Private hallway": "Pasillo privado",
  89. "Mini": "Mini",
  90. "cinema": "Cine",
  91. "River view": "Vista al río",
  92. "Double glazing windows": "Ventanas de doble acristalamiento",
  93. "Children's game room": "Sala de juegos para niños",
  94. "Heated Pool": "Piscina climatizada",
  95. "Kitchen": "Cocina",
  96. "Dining": "Comedor",
  97. "Hall": "Vestíbulo",
  98. "Living": "Living",
  99. "Individual heating": "Calefacción individual",
  100. "Cable": "Cable",
  101. "Furniture": "Amoblado",
  102. "Drinking water": "Agua potable",
  103. "Wifi": "WiFi",
  104. "Garden": "Jardín",
  105. "Laundry": "Lavadero",
  106. "Dining lounge": "Comedor diario",
  107. "Pets allowed": "Se permiten mascotas",
  108. "Work-friendly": "Apto para trabajar",
  109. "Bright": "Luminoso",
  110. "Gallery": "Galería",
  111. "Suite": "Suite",
  112. "Office": "Oficina",
  113. "Individual air conditioner": "Aire acondicionado individual",
  114. "Heating": "Calefacción",
  115. "Pool": "Piscina",
  116. "Balcony": "Balcón",
  117. "Patio": "Patio",
  118. "Air Heating": "Calefacción por aire",
  119. "Alarm": "Alarma",
  120. "Barbecue": "Parrilla",
  121. "Security": "Seguridad"
  122. };
  123.  
  124. // Mapeo para has_temporary_rent
  125. const temporaryRentTranslations = {
  126. "0": "-",
  127. "1": "Por día",
  128. "2": "Para fin de semana",
  129. "3": "Por semana",
  130. "4": "Quinceañera",
  131. "5": "Yo",
  132. "6": "1er quincena de enero",
  133. "7": "2da quincena de enero",
  134. "8": "1er quincena de febrero",
  135. "9": "2da quincena de febrero",
  136. "10": "1er quincena de marzo",
  137. "11": "2da quincena de marzo",
  138. "12": "Enero",
  139. "13": "Febrero",
  140. "14": "Marzo",
  141. "15": "Abril",
  142. "17": "Mayonesa",
  143. "18": "Junio",
  144. "19": "Julio",
  145. "20": "Agosto",
  146. "21": "Septiembre",
  147. "22": "Octubre",
  148. "23": "Noviembre",
  149. "24": "Diciembre",
  150. "25": "Por temporada",
  151. "26": "Por año",
  152. "27": "Fin de año",
  153. "28": "Semana Santa",
  154. "29": "1er quince de diciembre",
  155. "30": "2da quincena de diciembre",
  156. "false": "NO",
  157. "true": "SI"
  158. };
  159.  
  160.  
  161. // Orden personalizado de las columnas
  162. const headerOrder = [
  163. "id",
  164. "publication_title",
  165. "type",
  166. "fake_address",
  167. "location",
  168. "description",
  169. "tags",
  170. "extra_attributes",
  171. "age",
  172. "property_condition",
  173. "floors_amount",
  174. "suite_amount",
  175. "room_amount",
  176. "toilet_amount",
  177. "bathroom_amount",
  178. "parking_lot_amount",
  179. "disposition",
  180. "unroofed_surface",
  181. "total_surface",
  182. "surface",
  183. "semiroofed_surface",
  184. "roofed_surface",
  185. "depth_measure",
  186. "front_measure",
  187. "development",
  188. "development_excel_extra_data",
  189. "operation_currency",
  190. "operation_price",
  191. "operation_type",
  192. "expenses",
  193. "has_temporary_rent",
  194. "situation",
  195. "transaction_requirements",
  196. "zonification",
  197. "producer",
  198. "public_url",
  199. "videos",
  200. "reference_code"
  201. ];
  202.  
  203. // Limpiar la hoja
  204. sheet.clear();
  205.  
  206. let offset = 0;
  207. let firstPage = true;
  208. let headers = [];
  209.  
  210. // Realizar solicitudes paginadas
  211. while (offset < totalProperties) {
  212. const url = `${baseUrl}&limit=${limit}&offset=${offset}`;
  213. const options = {
  214. method: "GET",
  215. headers: {
  216. "Authorization": apiKey,
  217. },
  218. };
  219.  
  220. const response = UrlFetchApp.fetch(url, options);
  221. const data = JSON.parse(response.getContentText());
  222.  
  223. // Filtrar solo las propiedades con status = 2
  224. const filteredData = {
  225. ...data,
  226. objects: data.objects.filter(item => item.status === 2)
  227. };
  228.  
  229. if (firstPage) {
  230. // Filtrar las columnas no deseadas y excluir 'operations'
  231. headers = Object.keys(filteredData.objects[0]).filter(
  232. (header) => ![
  233. "branch", "created_at", "deleted_at", "files", "geo_lat", "geo_long",
  234. "gm_location_type", "occupation", "orientation", "portal_footer", "status",
  235. "web_price", "custom1", "is_denounced", "is_starred_on_web", "legally_checked",
  236. "real_address", "custom_tags", "address", "ID", "surface_measurement", "internal_data",
  237. "footer", "description_only", "photos", "rich_description", "operations"
  238. ].includes(header)
  239. );
  240.  
  241. // Agregar las nuevas columnas de operaciones
  242. headers.push("operation_currency");
  243. headers.push("operation_price");
  244. headers.push("operation_type");
  245.  
  246. // Ordenar headers según el orden personalizado
  247. headers.sort((a, b) => {
  248. return headerOrder.indexOf(a) - headerOrder.indexOf(b);
  249. });
  250.  
  251. // Obtener encabezados traducidos y agregarlos a la hoja
  252. const translatedHeaders = headers.map(
  253. (header) => headerTranslations[header] || header
  254. );
  255. sheet.appendRow(translatedHeaders);
  256. firstPage = false;
  257. }
  258.  
  259. // Insertar datos en la hoja
  260. filteredData.objects.forEach((item) => {
  261. const row = headers.map((header) => {
  262. // Procesar las columnas de operaciones
  263. if (["operation_currency", "operation_price", "operation_type"].includes(header)) {
  264. if (Array.isArray(item.operations) && item.operations.length > 0) {
  265. const firstOperation = item.operations[0];
  266. const priceInfo = firstOperation.prices?.[0];
  267.  
  268. if (header === "operation_currency") {
  269. return priceInfo?.currency || "";
  270. } else if (header === "operation_price") {
  271. return priceInfo?.price || "";
  272. } else if (header === "operation_type") {
  273. let operationType = firstOperation.operation_type;
  274. // Convertir a número si es posible
  275. const operationTypeNum = parseInt(operationType);
  276. if (!isNaN(operationTypeNum)) {
  277. // Si es un número, usar el mapeo numérico
  278. switch (operationTypeNum) {
  279. case 1: return "venta";
  280. case 2: return "alquiler";
  281. case 3: return "alquiler temporal";
  282. default: return operationType || "";
  283. }
  284. } else {
  285. // Mantener el mapeo de strings por compatibilidad
  286. if (operationType === "Sale") return "venta";
  287. if (operationType === "Rent") return "alquiler";
  288. return operationType || "";
  289. }
  290. }
  291. }
  292. return "";
  293. }
  294.  
  295. const value = item[header];
  296.  
  297. // Procesar age según los nuevos requerimientos
  298. if (header === "age") {
  299. if (value === -1) return "en construccion";
  300. if (value === 0) return "a estrenar";
  301. return value ? `${value} años` : "";
  302. }
  303.  
  304. if (header === "location" && typeof value === "object" && value !== null) {
  305. return value.full_location || "";
  306. }
  307. else if (header === "type" && typeof value === "object" && value !== null) {
  308. return typeTranslations[value.name] || value.name;
  309. }
  310. else if (header === "has_temporary_rent") {
  311. // Manejar tanto valores numéricos como booleanos
  312. const rentValue = String(value);
  313. Logger.log(`has_temporary_rent valor original: ${value}, tipo: ${typeof value}`);
  314. return temporaryRentTranslations[rentValue] || "-";
  315. }
  316. else if (header === "property_condition") {
  317. if (value === "Excellent") return "Excelente";
  318. if (value === "Good") return "Bueno";
  319. if (value === "Very good") return "Muy bueno";
  320. if (value === "To refurbish") return "A refaccionar";
  321. return value;
  322. }
  323. else if (header === "situation") {
  324. if (value === "In use") return "En uso";
  325. if (value === "Empty") return "Vacío";
  326. return value;
  327. }
  328. else if (
  329. header === "depth_measure" ||
  330. header === "front_measure" ||
  331. header === "surface_measurement"
  332. ) {
  333. return value ? `${value} mt.` : "";
  334. }
  335. else if (
  336. header === "roofed_surface" ||
  337. header === "semiroofed_surface" ||
  338. header === "surface" ||
  339. header === "total_surface" ||
  340. header === "unroofed_surface"
  341. ) {
  342. return value ? `${value} mts2` : "";
  343. }
  344. else if (header === "development") {
  345. return value ? "SI" : "NO";
  346. }
  347. else if (header === "disposition") {
  348. switch (value) {
  349. case "Front": return "Frente";
  350. case "Internal": return "Interno";
  351. case "BackFront": return "Contrafrente";
  352. default: return value;
  353. }
  354. }
  355. else if (header === "expenses") {
  356. return value ? `$${value}` : "";
  357. }
  358. else if (header === "videos" && Array.isArray(value)) {
  359. return value.map(video => video.url).join(" - ");
  360. }
  361. else if (header === "extra_attributes" && Array.isArray(value)) {
  362. return value
  363. .map(attribute => `${attribute.name.trim()} ${attribute.value || ""}`.trim())
  364. .join(" - ");
  365. }
  366. else if (header === "tags" && Array.isArray(value)) {
  367. return value
  368. .map(tag => translations[tag.name] || tag.name)
  369. .join(" - ");
  370. }
  371. else if (Array.isArray(value)) {
  372. return value.map(obj => JSON.stringify(obj)).join(", ");
  373. }
  374. else {
  375. return value;
  376. }
  377. });
  378. sheet.appendRow(row);
  379. });
  380.  
  381. offset += limit;
  382.  
  383. if (filteredData.objects.length < limit) {
  384. break;
  385. }
  386. }
  387. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement