From a7792f5dbb6166239ed27e254bdfb887eb9154c2 Mon Sep 17 00:00:00 2001 From: "Phyks (Lucas Verney)" Date: Wed, 12 Sep 2018 13:51:53 +0200 Subject: [PATCH] Rework upvotes Upvotes now reset the `datetime` field of the report, making it appear as if it was newly reported. They also extend the lifetime for accidents and GCUM. This introduces database migrations as well. Closes https://framagit.org/phyks/cyclassist/issues/41. --- README.md | 6 ++++++ scripts/migrations/0.3.py | 41 +++++++++++++++++++++++++++++++++++++++ server/models.py | 8 ++++++-- server/routes.py | 15 +++++++++++--- server/tools.py | 5 +++++ src/constants.js | 4 ++-- src/store/getters.js | 8 ++++---- 7 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 scripts/migrations/0.3.py create mode 100644 server/tools.py diff --git a/README.md b/README.md index 2315203..a323132 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,12 @@ python -m server to spawn the server-side part, listening on `localhost:8081`. +### Updating + +Database migrations are in the `scripts/migrations` folder, labelled by +versions. You should run them in order from your current versions to the +latest one when you upgrade. + ### Useful scripts for dev You can run `scripts/gps_to_gpx.py` on your GPX trace to create a diff --git a/scripts/migrations/0.3.py b/scripts/migrations/0.3.py new file mode 100644 index 0000000..60a1c14 --- /dev/null +++ b/scripts/migrations/0.3.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +""" +Database migration from < 0.3 to 0.3 version. +""" +import os +import sys + +import peewee + +SCRIPT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.abspath(os.path.join(SCRIPT_DIRECTORY, '..', '..'))) + +from playhouse.migrate import * + +from server.models import db, Report +from server.tools import UTC_now + + +def run_migration(): + if type(db) == peewee.SqliteDatabase: + migrator = SqliteMigrator(db) + elif type(db) == peewee.MySQLDatabase: + migrator = MySQLMigrator(db) + elif type(db) == peewee.PostgresqlDatabase: + migrator = PostgresqlMigrator(db) + else: + return + + migrate( + migrator.add_column('report', 'first_report_datetime', + peewee.DateTimeField(default=UTC_now)), + ) + query = Report.select() + for report in query: + report.first_report_datetime = report.datetime + report.save() + + +if __name__ == '__main__': + db.connect() + run_migration() diff --git a/server/models.py b/server/models.py index bf1b4b6..458f9ca 100644 --- a/server/models.py +++ b/server/models.py @@ -5,12 +5,13 @@ Models and database definition """ import os -import arrow import bottle import peewee from playhouse.db_url import connect from playhouse.shortcuts import model_to_dict +from server.tools import UTC_now + db = connect(os.environ.get('DATABASE', 'sqlite:///reports.db')) @@ -40,8 +41,11 @@ class Report(BaseModel): type = peewee.CharField(max_length=255) lat = peewee.DoubleField() lng = peewee.DoubleField() + first_report_datetime = peewee.DateTimeField( + default=UTC_now + ) datetime = peewee.DateTimeField( - default=lambda: arrow.utcnow().replace(microsecond=0).naive + default=UTC_now ) expiration_datetime = peewee.DateTimeField(null=True) is_open = peewee.BooleanField(default=True) diff --git a/server/routes.py b/server/routes.py index 2eb4554..44c36d2 100644 --- a/server/routes.py +++ b/server/routes.py @@ -10,6 +10,7 @@ import os import bottle from server.models import Report +from server.tools import UTC_now from server import jsonapi @@ -80,7 +81,7 @@ def get_reports(only_active=False): if only_active: query = query.where( (Report.expiration_datetime == None) | - (Report.expiration_datetime > arrow.utcnow().replace(microsecond=0).datetime) + (Report.expiration_datetime > UTC_now()) ) query = query.order_by(*sorting) if page_number and page_size: @@ -195,7 +196,7 @@ def post_report(): # Handle expiration if r.type in ['accident', 'gcum']: r.expiration_datetime = ( - arrow.utcnow().replace(microsecond=0).shift(hours=+1).datetime + arrow.get(UTC_now()).shift(hours=+1).naive ) r.save() except KeyError as exc: @@ -228,7 +229,15 @@ def upvote_report(id): r = Report.get(Report.id == id) if not r: return jsonapi.JsonApiError(404, "Invalid report id.") + # Increase upvotes r.upvotes += 1 + # Update report datetime + r.datetime = UTC_now() + # Update expiration datetime + if r.type in ['accident', 'gcum']: + r.expiration_datetime = ( + arrow.get(UTC_now()).shift(hours=+1).naive + ) r.save() return { @@ -282,7 +291,7 @@ def get_stats(): nb_reports = Report.select().count() nb_active_reports = Report.select().where( (Report.expiration_datetime == None) | - (Report.expiration_datetime > arrow.utcnow().replace(microsecond=0).datetime) + (Report.expiration_datetime > UTC_now()) ).count() last_added_report_datetime = Report.select().order_by( Report.datetime.desc() diff --git a/server/tools.py b/server/tools.py new file mode 100644 index 0000000..d925e6a --- /dev/null +++ b/server/tools.py @@ -0,0 +1,5 @@ +import arrow + + +def UTC_now(): + return arrow.utcnow().replace(microsecond=0).naive diff --git a/src/constants.js b/src/constants.js index a6e861f..04228f4 100644 --- a/src/constants.js +++ b/src/constants.js @@ -85,8 +85,8 @@ export const MOCK_LOCATION_LNG_MAX = 2.392742; export const UPDATE_REPORTS_DISTANCE_THRESHOLD = 500; // in meters -// Minimal ratio between upvotes and downvotes needed for a report to be shown -export const REPORT_VOTES_THRESHOLD = 0.5; +// Minimal number of downvotes needed for a report to be masked +export const REPORT_DOWNVOTES_THRESHOLD = 1; export const EARTH_RADIUS = 6378137; // in meters diff --git a/src/store/getters.js b/src/store/getters.js index 0e99404..89131bb 100644 --- a/src/store/getters.js +++ b/src/store/getters.js @@ -1,4 +1,4 @@ -import { REPORT_VOTES_THRESHOLD } from '@/constants'; +import { REPORT_DOWNVOTES_THRESHOLD } from '@/constants'; export function getLastLocation(state) { const { gpx } = state.location; @@ -10,9 +10,9 @@ export function getLastLocation(state) { export function notDismissedReports(state) { return state.reports.filter((item) => { - if (item.attributes.downvotes === 0) { - return true; + if (item.attributes.downvotes >= REPORT_DOWNVOTES_THRESHOLD) { + return false; } - return (item.attributes.upvotes / item.attributes.downvotes) > REPORT_VOTES_THRESHOLD; + return true; }); }