Spaces:
Running
Running
telemetry re-format
Browse files- app.py +2 -0
- models.py +35 -0
- static/js/components/capture-form.js +78 -3
- static/js/components/confirmation.js +24 -4
- static/js/components/history.js +6 -3
app.py
CHANGED
|
@@ -60,6 +60,8 @@ def sync_captures():
|
|
| 60 |
for c in captures_data:
|
| 61 |
# Explicitly log that these are device coordinates being forwarded
|
| 62 |
print(f" [DEVICE SYNC] Capture {c.get('id')}:")
|
|
|
|
|
|
|
| 63 |
print(f" - Latitude: {c.get('latitude')} (captured on device)")
|
| 64 |
print(f" - Longitude: {c.get('longitude')} (captured on device)")
|
| 65 |
print(f" - Place: {c.get('placeName')}")
|
|
|
|
| 60 |
for c in captures_data:
|
| 61 |
# Explicitly log that these are device coordinates being forwarded
|
| 62 |
print(f" [DEVICE SYNC] Capture {c.get('id')}:")
|
| 63 |
+
print(f" - Fisherman: {c.get('fishermanName')} ({c.get('idCard')})")
|
| 64 |
+
print(f" - Port: {c.get('port')}")
|
| 65 |
print(f" - Latitude: {c.get('latitude')} (captured on device)")
|
| 66 |
print(f" - Longitude: {c.get('longitude')} (captured on device)")
|
| 67 |
print(f" - Place: {c.get('placeName')}")
|
models.py
CHANGED
|
@@ -72,6 +72,18 @@ class Capture:
|
|
| 72 |
fishingMethod: str
|
| 73 |
depth: float
|
| 74 |
synced: bool
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
userId: Optional[str] = None
|
| 76 |
placeName: Optional[str] = None
|
| 77 |
syncedAt: Optional[datetime] = None
|
|
@@ -90,6 +102,16 @@ class Capture:
|
|
| 90 |
'fishingMethod': self.fishingMethod,
|
| 91 |
'depth': self.depth,
|
| 92 |
'synced': self.synced,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
'userId': self.userId,
|
| 94 |
'placeName': self.placeName,
|
| 95 |
'syncedAt': self.syncedAt.isoformat() if self.syncedAt and isinstance(self.syncedAt, datetime) else self.syncedAt,
|
|
@@ -125,6 +147,19 @@ class Capture:
|
|
| 125 |
fishingMethod=data.get('fishingMethod'),
|
| 126 |
depth=data.get('depth'),
|
| 127 |
synced=data.get('synced', False),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
userId=data.get('userId'),
|
| 129 |
placeName=data.get('placeName'),
|
| 130 |
syncedAt=syncedAt,
|
|
|
|
| 72 |
fishingMethod: str
|
| 73 |
depth: float
|
| 74 |
synced: bool
|
| 75 |
+
# New fields from user request
|
| 76 |
+
fishermanName: Optional[str] = None
|
| 77 |
+
idCard: Optional[str] = None
|
| 78 |
+
phone: Optional[str] = None
|
| 79 |
+
association: Optional[str] = None
|
| 80 |
+
port: Optional[str] = None
|
| 81 |
+
boatDetails: Optional[str] = None
|
| 82 |
+
engineDetails: Optional[str] = None
|
| 83 |
+
fishingGear: Optional[str] = None
|
| 84 |
+
salePrice: Optional[float] = None
|
| 85 |
+
buyer: Optional[str] = None
|
| 86 |
+
|
| 87 |
userId: Optional[str] = None
|
| 88 |
placeName: Optional[str] = None
|
| 89 |
syncedAt: Optional[datetime] = None
|
|
|
|
| 102 |
'fishingMethod': self.fishingMethod,
|
| 103 |
'depth': self.depth,
|
| 104 |
'synced': self.synced,
|
| 105 |
+
'fishermanName': self.fishermanName,
|
| 106 |
+
'idCard': self.idCard,
|
| 107 |
+
'phone': self.phone,
|
| 108 |
+
'association': self.association,
|
| 109 |
+
'port': self.port,
|
| 110 |
+
'boatDetails': self.boatDetails,
|
| 111 |
+
'engineDetails': self.engineDetails,
|
| 112 |
+
'fishingGear': self.fishingGear,
|
| 113 |
+
'salePrice': self.salePrice,
|
| 114 |
+
'buyer': self.buyer,
|
| 115 |
'userId': self.userId,
|
| 116 |
'placeName': self.placeName,
|
| 117 |
'syncedAt': self.syncedAt.isoformat() if self.syncedAt and isinstance(self.syncedAt, datetime) else self.syncedAt,
|
|
|
|
| 147 |
fishingMethod=data.get('fishingMethod'),
|
| 148 |
depth=data.get('depth'),
|
| 149 |
synced=data.get('synced', False),
|
| 150 |
+
|
| 151 |
+
# New fields
|
| 152 |
+
fishermanName=data.get('fishermanName'),
|
| 153 |
+
idCard=data.get('idCard'),
|
| 154 |
+
phone=data.get('phone'),
|
| 155 |
+
association=data.get('association'),
|
| 156 |
+
port=data.get('port'),
|
| 157 |
+
boatDetails=data.get('boatDetails'),
|
| 158 |
+
engineDetails=data.get('engineDetails'),
|
| 159 |
+
fishingGear=data.get('fishingGear'),
|
| 160 |
+
salePrice=data.get('salePrice'),
|
| 161 |
+
buyer=data.get('buyer'),
|
| 162 |
+
|
| 163 |
userId=data.get('userId'),
|
| 164 |
placeName=data.get('placeName'),
|
| 165 |
syncedAt=syncedAt,
|
static/js/components/capture-form.js
CHANGED
|
@@ -25,6 +25,58 @@ const CaptureFormComponent = {
|
|
| 25 |
</div>
|
| 26 |
|
| 27 |
<form id="capture-form" onsubmit="CaptureFormComponent.submit(event)">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
${selectedSpecies.map((speciesId, index) => `
|
| 29 |
<div class="card">
|
| 30 |
${speciesId === 'otro' ? `
|
|
@@ -42,7 +94,6 @@ const CaptureFormComponent = {
|
|
| 42 |
`}
|
| 43 |
|
| 44 |
<div class="form-group">
|
| 45 |
-
...
|
| 46 |
<label for="quantity-${index}">Cantidad</label>
|
| 47 |
<input
|
| 48 |
type="number"
|
|
@@ -65,8 +116,9 @@ const CaptureFormComponent = {
|
|
| 65 |
`).join('')}
|
| 66 |
|
| 67 |
<div class="card">
|
|
|
|
| 68 |
<div class="form-group">
|
| 69 |
-
<label for="fishing-method">Método de Pesca</label>
|
| 70 |
<select id="fishing-method" name="fishing-method" required>
|
| 71 |
<option value="">Seleccionar...</option>
|
| 72 |
<option value="Línea de mano">Línea de mano</option>
|
|
@@ -89,10 +141,20 @@ const CaptureFormComponent = {
|
|
| 89 |
required
|
| 90 |
>
|
| 91 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
</div>
|
| 93 |
|
| 94 |
<button type="submit" class="btn-primary btn-full">
|
| 95 |
-
Guardar
|
| 96 |
</button>
|
| 97 |
</form>
|
| 98 |
</div>
|
|
@@ -179,6 +241,19 @@ const CaptureFormComponent = {
|
|
| 179 |
items: items,
|
| 180 |
fishingMethod: document.getElementById('fishing-method').value,
|
| 181 |
depth: parseFloat(document.getElementById('depth').value),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
totalWeight: items.reduce((sum, item) => sum + (item.unit === 'lbs' ? item.quantity : 0), 0),
|
| 183 |
captureSource: 'device-gps',
|
| 184 |
synced: 0
|
|
|
|
| 25 |
</div>
|
| 26 |
|
| 27 |
<form id="capture-form" onsubmit="CaptureFormComponent.submit(event)">
|
| 28 |
+
<!-- Datos Personales -->
|
| 29 |
+
<div class="card">
|
| 30 |
+
<h3>👤 Datos del Pescador</h3>
|
| 31 |
+
<div class="form-group">
|
| 32 |
+
<label for="fisherman-name">Nombre Completo</label>
|
| 33 |
+
<input type="text" id="fisherman-name" name="fisherman-name" required>
|
| 34 |
+
</div>
|
| 35 |
+
<div class="form-group">
|
| 36 |
+
<label for="id-card">Cédula</label>
|
| 37 |
+
<input type="text" id="id-card" name="id-card" placeholder="000-0000000-0">
|
| 38 |
+
</div>
|
| 39 |
+
<div class="form-group">
|
| 40 |
+
<label for="phone">Teléfono</label>
|
| 41 |
+
<input type="tel" id="phone" name="phone">
|
| 42 |
+
</div>
|
| 43 |
+
<div class="form-group">
|
| 44 |
+
<label for="association">Asociación/Cooperativa</label>
|
| 45 |
+
<input type="text" id="association" name="association">
|
| 46 |
+
</div>
|
| 47 |
+
</div>
|
| 48 |
+
|
| 49 |
+
<!-- Datos de la Embarcación y Arte -->
|
| 50 |
+
<div class="card">
|
| 51 |
+
<h3>🚤 Embarcación y Equipo</h3>
|
| 52 |
+
<div class="form-group">
|
| 53 |
+
<label for="port">Puerto / Desembarcadero</label>
|
| 54 |
+
<input type="text" id="port" name="port" required>
|
| 55 |
+
</div>
|
| 56 |
+
<div class="form-group">
|
| 57 |
+
<label for="boat-details">Nombre/Matrícula Embarcación</label>
|
| 58 |
+
<input type="text" id="boat-details" name="boat-details">
|
| 59 |
+
</div>
|
| 60 |
+
<div class="form-group">
|
| 61 |
+
<label for="engine-details">Motor (HP/Marca)</label>
|
| 62 |
+
<input type="text" id="engine-details" name="engine-details">
|
| 63 |
+
</div>
|
| 64 |
+
<div class="form-group">
|
| 65 |
+
<label for="fishing-gear">Arte de Pesca Principal</label>
|
| 66 |
+
<select id="fishing-gear" name="fishing-gear" required>
|
| 67 |
+
<option value="">Seleccionar...</option>
|
| 68 |
+
<option value="Línea de mano">Línea de mano</option>
|
| 69 |
+
<option value="Nasa">Nasa</option>
|
| 70 |
+
<option value="Red de enmalle">Red de enmalle</option>
|
| 71 |
+
<option value="Palangre">Palangre</option>
|
| 72 |
+
<option value="Buceo">Buceo</option>
|
| 73 |
+
<option value="Otros">Otros</option>
|
| 74 |
+
</select>
|
| 75 |
+
</div>
|
| 76 |
+
</div>
|
| 77 |
+
|
| 78 |
+
<!-- Detalles de Captura por Especie -->
|
| 79 |
+
<h3>🐟 Captura Específica</h3>
|
| 80 |
${selectedSpecies.map((speciesId, index) => `
|
| 81 |
<div class="card">
|
| 82 |
${speciesId === 'otro' ? `
|
|
|
|
| 94 |
`}
|
| 95 |
|
| 96 |
<div class="form-group">
|
|
|
|
| 97 |
<label for="quantity-${index}">Cantidad</label>
|
| 98 |
<input
|
| 99 |
type="number"
|
|
|
|
| 116 |
`).join('')}
|
| 117 |
|
| 118 |
<div class="card">
|
| 119 |
+
<h3>📉 Otros Detalles</h3>
|
| 120 |
<div class="form-group">
|
| 121 |
+
<label for="fishing-method">Método de Pesca (General)</label>
|
| 122 |
<select id="fishing-method" name="fishing-method" required>
|
| 123 |
<option value="">Seleccionar...</option>
|
| 124 |
<option value="Línea de mano">Línea de mano</option>
|
|
|
|
| 141 |
required
|
| 142 |
>
|
| 143 |
</div>
|
| 144 |
+
|
| 145 |
+
<div class="form-group">
|
| 146 |
+
<label for="sale-price">Precio de Venta Total (RD$)</label>
|
| 147 |
+
<input type="number" id="sale-price" name="sale-price" min="0" step="0.01">
|
| 148 |
+
</div>
|
| 149 |
+
|
| 150 |
+
<div class="form-group">
|
| 151 |
+
<label for="buyer">Comprador / Destino</label>
|
| 152 |
+
<input type="text" id="buyer" name="buyer">
|
| 153 |
+
</div>
|
| 154 |
</div>
|
| 155 |
|
| 156 |
<button type="submit" class="btn-primary btn-full">
|
| 157 |
+
Guardar Registro de Pesca
|
| 158 |
</button>
|
| 159 |
</form>
|
| 160 |
</div>
|
|
|
|
| 241 |
items: items,
|
| 242 |
fishingMethod: document.getElementById('fishing-method').value,
|
| 243 |
depth: parseFloat(document.getElementById('depth').value),
|
| 244 |
+
|
| 245 |
+
// New fields
|
| 246 |
+
fishermanName: document.getElementById('fisherman-name').value,
|
| 247 |
+
idCard: document.getElementById('id-card').value,
|
| 248 |
+
phone: document.getElementById('phone').value,
|
| 249 |
+
association: document.getElementById('association').value,
|
| 250 |
+
port: document.getElementById('port').value,
|
| 251 |
+
boatDetails: document.getElementById('boat-details').value,
|
| 252 |
+
engineDetails: document.getElementById('engine-details').value,
|
| 253 |
+
fishingGear: document.getElementById('fishing-gear').value,
|
| 254 |
+
salePrice: parseFloat(document.getElementById('sale-price').value) || 0,
|
| 255 |
+
buyer: document.getElementById('buyer').value,
|
| 256 |
+
|
| 257 |
totalWeight: items.reduce((sum, item) => sum + (item.unit === 'lbs' ? item.quantity : 0), 0),
|
| 258 |
captureSource: 'device-gps',
|
| 259 |
synced: 0
|
static/js/components/confirmation.js
CHANGED
|
@@ -42,6 +42,21 @@ const ConfirmationComponent = {
|
|
| 42 |
<h3>Resumen de Captura</h3>
|
| 43 |
|
| 44 |
<dl>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
<dt>📅 Fecha y Hora</dt>
|
| 46 |
<dd>${this.formatDate(capture.timestamp)}</dd>
|
| 47 |
|
|
@@ -59,10 +74,15 @@ const ConfirmationComponent = {
|
|
| 59 |
${item.quantity} ${item.unit === 'lbs' ? 'libras' : 'unidades'}
|
| 60 |
`).join('<br>')}
|
| 61 |
</dd>
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
<dt>📏 Profundidad</dt>
|
| 67 |
<dd>${capture.depth} brazadas</dd>
|
| 68 |
</dl>
|
|
|
|
| 42 |
<h3>Resumen de Captura</h3>
|
| 43 |
|
| 44 |
<dl>
|
| 45 |
+
<dt>👤 Pescador</dt>
|
| 46 |
+
<dd>
|
| 47 |
+
<strong>${capture.fishermanName}</strong><br>
|
| 48 |
+
${capture.idCard ? `Cédula: ${capture.idCard}<br>` : ''}
|
| 49 |
+
${capture.phone ? `Tel: ${capture.phone}<br>` : ''}
|
| 50 |
+
${capture.association ? `Asociación: ${capture.association}` : ''}
|
| 51 |
+
</dd>
|
| 52 |
+
|
| 53 |
+
<dt>🚤 Embarcación y Arte</dt>
|
| 54 |
+
<dd>
|
| 55 |
+
<strong>Puerto: ${capture.port}</strong><br>
|
| 56 |
+
${capture.boatDetails ? `Barco: ${capture.boatDetails}<br>` : ''}
|
| 57 |
+
${capture.fishingGear ? `Arte: ${capture.fishingGear}` : ''}
|
| 58 |
+
</dd>
|
| 59 |
+
|
| 60 |
<dt>📅 Fecha y Hora</dt>
|
| 61 |
<dd>${this.formatDate(capture.timestamp)}</dd>
|
| 62 |
|
|
|
|
| 74 |
${item.quantity} ${item.unit === 'lbs' ? 'libras' : 'unidades'}
|
| 75 |
`).join('<br>')}
|
| 76 |
</dd>
|
| 77 |
+
|
| 78 |
+
${capture.salePrice ? `
|
| 79 |
+
<dt>💰 Venta</dt>
|
| 80 |
+
<dd>
|
| 81 |
+
RD$ ${capture.salePrice.toLocaleString()}<br>
|
| 82 |
+
${capture.buyer ? `Comprador: ${capture.buyer}` : ''}
|
| 83 |
+
</dd>
|
| 84 |
+
` : ''}
|
| 85 |
+
|
| 86 |
<dt>📏 Profundidad</dt>
|
| 87 |
<dd>${capture.depth} brazadas</dd>
|
| 88 |
</dl>
|
static/js/components/history.js
CHANGED
|
@@ -40,9 +40,12 @@ const HistoryComponent = {
|
|
| 40 |
.map(capture => `
|
| 41 |
<li class="history-item ${capture.synced === 1 ? 'synced' : 'unsynced'}">
|
| 42 |
<h3>
|
| 43 |
-
${
|
| 44 |
-
|
| 45 |
</h3>
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
<p>
|
| 48 |
<strong>Especies:</strong>
|
|
@@ -52,7 +55,7 @@ const HistoryComponent = {
|
|
| 52 |
</p>
|
| 53 |
|
| 54 |
<p>
|
| 55 |
-
<strong>
|
| 56 |
<strong>Profundidad:</strong> ${capture.depth} brazadas
|
| 57 |
</p>
|
| 58 |
|
|
|
|
| 40 |
.map(capture => `
|
| 41 |
<li class="history-item ${capture.synced === 1 ? 'synced' : 'unsynced'}">
|
| 42 |
<h3>
|
| 43 |
+
${capture.fishermanName || 'Pescador'} @ ${capture.port || 'Puerto'}
|
| 44 |
+
<span style="float: right;">${capture.synced === 1 ? '✅' : '⏳'}</span>
|
| 45 |
</h3>
|
| 46 |
+
<p style="font-size: 0.9rem; color: #444; margin-bottom: 5px;">
|
| 47 |
+
${this.formatDate(capture.timestamp)}
|
| 48 |
+
</p>
|
| 49 |
|
| 50 |
<p>
|
| 51 |
<strong>Especies:</strong>
|
|
|
|
| 55 |
</p>
|
| 56 |
|
| 57 |
<p>
|
| 58 |
+
<strong>Arte:</strong> ${capture.fishingGear || capture.fishingMethod} |
|
| 59 |
<strong>Profundidad:</strong> ${capture.depth} brazadas
|
| 60 |
</p>
|
| 61 |
|