diff --git a/imports/api/FalsePositives/FalsePositiveTypes.js b/imports/api/FalsePositives/FalsePositiveTypes.js new file mode 100644 index 0000000..9c8b5fb --- /dev/null +++ b/imports/api/FalsePositives/FalsePositiveTypes.js @@ -0,0 +1,7 @@ +const types = { + industry: 'Es una industria (altos hornos, incineradora, etc)', + controled: 'Es una quema controlada', + falsealarm: 'Parece una falsa alarma' +}; + +export default types; diff --git a/imports/api/FalsePositives/FalsePositives.js b/imports/api/FalsePositives/FalsePositives.js new file mode 100644 index 0000000..592fe12 --- /dev/null +++ b/imports/api/FalsePositives/FalsePositives.js @@ -0,0 +1,37 @@ +/* eslint-disable consistent-return */ + +import { Meteor } from 'meteor/meteor'; +import { Mongo } from 'meteor/mongo'; +import SimpleSchema from 'simpl-schema'; +import { defaultCreatedAt, defaultUpdateAt } from '/imports/api/Utility/Utils.js'; +import LocationSchema from '/imports/api/Utility/LocationSchema.js'; + +const FalsePositives = new Mongo.Collection('falsePositives', { idGeneration: 'MONGO' }); + +FalsePositives.allow({ + insert: () => false, + update: () => false, + remove: () => false +}); + +FalsePositives.deny({ + insert: () => true, + update: () => true, + remove: () => true +}); + +FalsePositives.schema = new SimpleSchema({ + geo: LocationSchema, + fireId: { type: Meteor.Collection.ObjectID, optional: true, blackbox: true }, + chatId: { type: Number, optional: true }, // only in 'telegram' type + owner: String, + type: String, + when: Date, + whendateformat: String, + createdAt: defaultCreatedAt, + updatedAt: defaultUpdateAt +}); + +FalsePositives.attachSchema(FalsePositives.schema); + +export default FalsePositives; diff --git a/imports/api/FalsePositives/methods.js b/imports/api/FalsePositives/methods.js new file mode 100644 index 0000000..7aa4ef1 --- /dev/null +++ b/imports/api/FalsePositives/methods.js @@ -0,0 +1,53 @@ +import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; +import moment from 'moment'; +import FalsePositives from './FalsePositives'; +import FalsePositiveTypes from './FalsePositiveTypes'; +import Fires from '../Fires/Fires'; +import rateLimit from '../../modules/rate-limit'; + +Meteor.methods({ + 'falsePositives.insert': function falsePositivesInsert(fireId, keyType) { + check(fireId, Meteor.Collection.ObjectID); + check(keyType, String); + const type = FalsePositiveTypes[keyType]; + check(type, String); + if (!this.userId) { + throw new Meteor.Error('500', 'Regístrate o inicia sesión para aportar información sobre este fuego'); + } + check(this.userId, String); + const fire = Fires.findOne(fireId); + if (fire) { + const fireType = fire.type; + if (fireType === 'vecinal') { + throw new Meteor.Error('500', 'No se puede marcar este tipo de fuego'); + } + const date = new Date(); + const formated = moment(date).format('YYYYMMDD'); + try { + const set = { + owner: this.userId, + type: keyType, + when: date, + whendateformat: formated, + fireId, + geo: fire.ourid + }; + return FalsePositives.upsert({ geo: fire.ourid, owner: this.userId }, { $set: set }, { multi: false, upsert: true }); + } catch (exception) { + console.log(exception); + throw new Meteor.Error('500', exception); + } + } else { + throw new Meteor.Error('500', 'Fuego no encontrado'); + } + } +}); + +rateLimit({ + methods: [ + 'falsePositives.insert' + ], + limit: 5, + timeRange: 1000 +}); diff --git a/imports/api/Fires/server/publications.js b/imports/api/Fires/server/publications.js index de67f8b..a7c0c43 100644 --- a/imports/api/Fires/server/publications.js +++ b/imports/api/Fires/server/publications.js @@ -30,13 +30,11 @@ Meteor.publish('fireFromHash', function fireFromHash(fireEnc) { // console.log(fireEnc); const unsealed = Promise.await(urlEnc.decrypt(fireEnc)); const w = unsealed.when; - // console.log(w); unsealed.when = new Date(w); - // console.log(unsealed.when); const c = unsealed.createdAt; - unsealed.createdAt = new Date(c); + unsealed.createdAt = !c ? new Date() : new Date(c); const u = unsealed.updatedAt; - unsealed.updatedAt = new Date(u); + unsealed.updatedAt = !u ? new Date() : new Date(u); // console.log(unsealed); FiresCollection.schema.validate(unsealed); const fire = findFire(unsealed); diff --git a/imports/startup/common/comments.js b/imports/startup/common/comments.js index 379f836..09fa297 100644 --- a/imports/startup/common/comments.js +++ b/imports/startup/common/comments.js @@ -9,7 +9,7 @@ Comments.config({ profile: 1 }, generateUsername: function genUser(user) { - console.log(JSON.stringify(user)); + // console.log(JSON.stringify(user)); // FIXME return user.profile && user.profile.name && user.profile.name.first ? user.profile.name.first : ''; } diff --git a/imports/startup/server/api.js b/imports/startup/server/api.js index 64b3e9f..0fcde5d 100644 --- a/imports/startup/server/api.js +++ b/imports/startup/server/api.js @@ -24,3 +24,5 @@ import '../../api/Fires/server/publications'; import '../../api/SiteSettings/methods'; import '../../api/SiteSettings/server/publications'; + +import '../../api/FalsePositives/methods'; diff --git a/imports/startup/server/migrations.js b/imports/startup/server/migrations.js index 1c49a4c..a4482f0 100644 --- a/imports/startup/server/migrations.js +++ b/imports/startup/server/migrations.js @@ -6,6 +6,7 @@ import randomHex from 'crypto-random-hex'; import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions'; import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts'; import SiteSettings from '/imports/api/SiteSettings/SiteSettings'; +import FalsePositives from '/imports/api/FalsePositives/FalsePositives'; Meteor.startup(() => { // https://github.com/percolatestudio/meteor-migrations @@ -92,6 +93,17 @@ Meteor.startup(() => { } }); + Migrations.add({ + version: 6, + up: function falsePositiveIndexes() { + FalsePositives._ensureIndex({ chatId: 1 }); + FalsePositives._ensureIndex({ owner: 1 }); + FalsePositives._ensureIndex({ fireId: 1 }); + FalsePositives._ensureIndex({ type: 1 }); + FalsePositives._ensureIndex({ geo: '2dsphere' }); + } + }); + // Set createdAt in users & subs Migrations.migrateTo('latest'); diff --git a/imports/ui/pages/Fires/Fires.js b/imports/ui/pages/Fires/Fires.js index 38c7aad..fce9edd 100644 --- a/imports/ui/pages/Fires/Fires.js +++ b/imports/ui/pages/Fires/Fires.js @@ -6,7 +6,9 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { withTracker } from 'meteor/react-meteor-data'; import { translate, Trans } from 'react-i18next'; +import { FormGroup } from 'react-bootstrap'; import { Meteor } from 'meteor/meteor'; +import { Bert } from 'meteor/themeteorchef:bert'; import { Map, Circle } from 'react-leaflet'; import Blaze from 'meteor/gadicc:blaze-react-component'; import DefMapLayers from '/imports/ui/components/Maps/DefMapLayers'; @@ -15,6 +17,7 @@ import FiresCollection from '/imports/api/Fires/Fires'; import FromNow from '/imports/ui/components/FromNow/FromNow'; import { dateLongFormat } from '/imports/api/Common/dates'; import '/imports/startup/client/comments'; +import FalsePositiveTypes from '/imports/api/FalsePositives/FalsePositiveTypes'; import './Fires.scss'; class Fire extends React.Component { @@ -38,14 +41,25 @@ class Fire extends React.Component { return !(nextState.when === this.state.when); } + onTypeSelect(key) { + // console.log(key); + Meteor.call('falsePositives.insert', this.props.fire._id, key, (error) => { + if (error) { + // console.log(error); + Bert.alert(this.props.t(error.reason), 'danger'); + } else { + Bert.alert(this.props.t('Tomamos nota, ¡gracias por colaborar!'), 'success'); + } + }); + } + render() { const { loading, fire, t } = this.props; if (fire && fire.when) { this.dateLongFormat = dateLongFormat(fire.when); - // this.dateFromNow = () => (); } - return (fire ? - (
+ return (fire ? ( +
{!loading &&

@@ -82,26 +96,54 @@ class Fire extends React.Component { {(fire.type === 'vecinal') &&

Fuego notificado por uno de nuestros usuarios/as

} - {/* TODO: marcar tipo de fuego, industria, etc */} -

{t('Comentarios')}

-
- {t('Puedes añadir un comentario si tienes información adicional sobre este fuego.')} - {' '} - {t('Por ejemplo:')} -
    -
  • {t('si conoces esta zona y cómo acceder al fuego (esto puede de ser de ayuda para apagarlo si sigue activo o para investigarlo en un futuro)')}
  • -
  • {t('si conoces el motivo por el que comenzó el fuego')}
  • -
  • {t('si quieres denunciar algún tipo de ilegalidad, incluso anónimamente')}
  • -
  • {t('o cualquier otra información')}
  • -
-
-
- -
+ + {(fire.type !== 'vecinal') && + +
{t('¿No es un fuego forestal?')}
+
+ Indícanos de que tipo de fuego se trata y ayúdanos así a mejorar nuestras notificaciones: +
+ +
+ +
+ {Object.keys(FalsePositiveTypes).map(key => ( + + )) + } +
+
+
+
} + +

{t('Comentarios')}

+
+ {t('Puedes añadir un comentario si tienes información adicional sobre este fuego.')} + {' '} + {t('Por ejemplo:')} +
    +
  • {t('si conoces esta zona y cómo acceder al fuego (esto puede de ser de ayuda para apagarlo si sigue activo o para investigarlo en un futuro)')}
  • +
  • {t('si conoces el motivo por el que comenzó el fuego')}
  • +
  • {t('si quieres denunciar algún tipo de ilegalidad, incluso anónimamente')}
  • +
  • {t('o cualquier otra información')}
  • +
+
+
+ +
} -
- ) : ); +
+ ) : ); } } @@ -109,7 +151,7 @@ Fire.propTypes = { t: PropTypes.func.isRequired, loading: PropTypes.bool.isRequired, when: PropTypes.instanceOf(Date), - fire: PropTypes.object // .isRequired + fire: PropTypes.object }; Fire.defaultProps = { @@ -117,7 +159,7 @@ Fire.defaultProps = { // export default translate([], { wait: true })(withTracker((props) => { -const FireContainer = withTracker(({ match, i18n }) => { +const FireContainer = withTracker(({ match }) => { const fireEncrypt = match.params.id; const subscription = Meteor.subscribe('fireFromHash', fireEncrypt); // console.log(`Subs ready: ${subscription.ready()}, fire: ${JSON.stringify(FiresCollection.findOne())}`); diff --git a/imports/ui/stylesheets/custom.scss b/imports/ui/stylesheets/custom.scss index 51d07cc..87e9027 100644 --- a/imports/ui/stylesheets/custom.scss +++ b/imports/ui/stylesheets/custom.scss @@ -36,3 +36,7 @@ h4.page-header { margin-top: 10px; } } + +#react-root > div > div.container > div > div.alert { + margin-top: 20px; +} \ No newline at end of file diff --git a/public/locales/es/common.json b/public/locales/es/common.json index b96bf67..068dfce 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -232,5 +232,13 @@ "Verifica tu dirección de correo", "Zonas vigiladas": "Zonas vigiladas", "En verde, las zonas vigiladas por nuestros usuari@s actualmente": "En verde, las zonas vigiladas por nuestros usuari@s actualmente", - "Datos actualizados <1>": "Datos actualizados <1>" + "Datos actualizados <1>": "Datos actualizados <1>", + "Tomamos nota, ¡gracias por colaborar! Tomamos nota, ¡gracias por colaborar!": + "Tomamos nota, ¡gracias por colaborar! Tomamos nota, ¡gracias por colaborar!", + "Indícanos de que tipo de fuego se trata y ayúdanos así a mejorar nuestras notificaciones:": "Indícanos de que tipo de fuego se trata y ayúdanos así a mejorar nuestras notificaciones:", + "¿No es un fuego forestal?": "¿No es un fuego forestal?", + "Regístrate o inicia sesión para aportar información sobre este fuego": + "Regístrate o inicia sesión para aportar información sobre este fuego", + "Fuego no encontrado": + "Fuego no encontrado" }