Add statistics about the instance under the About section.
This commit is contained in:
parent
aca68fb2e3
commit
d2bae9e532
@ -17,7 +17,11 @@ could know the location of the displayed map.
|
|||||||
|
|
||||||
The data collected by https://cyclo.phyks.me/ is available under an
|
The data collected by https://cyclo.phyks.me/ is available under an
|
||||||
[ODbL](https://opendatacommons.org/licenses/odbl/) license. You can get the
|
[ODbL](https://opendatacommons.org/licenses/odbl/) license. You can get the
|
||||||
most up to date JSON dump of available reports at https://cyclo.phyks.me/api/v1/reports.
|
most up to date JSON dump of available reports at
|
||||||
|
https://cyclo.phyks.me/api/v1/reports.
|
||||||
|
|
||||||
|
Statistics about the instance can be fetched at
|
||||||
|
https://cyclo.phyks.me/api/v1/stats.
|
||||||
|
|
||||||
## Hosting your own
|
## Hosting your own
|
||||||
|
|
||||||
@ -78,6 +82,9 @@ python -m server
|
|||||||
|
|
||||||
It is better to use a dedicated `virtualenv` if you can :)
|
It is better to use a dedicated `virtualenv` if you can :)
|
||||||
|
|
||||||
|
API routes are all listed within `server/routes.py` file, with documentation
|
||||||
|
strings.
|
||||||
|
|
||||||
#### Useful environment variables
|
#### Useful environment variables
|
||||||
|
|
||||||
You can pass a few environment variables to the `python -m server` command to
|
You can pass a few environment variables to the `python -m server` command to
|
||||||
|
@ -264,3 +264,34 @@ def downvote_report(id):
|
|||||||
return {
|
return {
|
||||||
"data": r.to_json()
|
"data": r.to_json()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@bottle.route('/api/v1/stats', ["GET", "OPTIONS"])
|
||||||
|
def get_stats():
|
||||||
|
"""
|
||||||
|
API v1 GET stats about this instance.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
GET /api/v1/states
|
||||||
|
"""
|
||||||
|
# Handle CORS
|
||||||
|
if bottle.request.method == 'OPTIONS':
|
||||||
|
return {}
|
||||||
|
|
||||||
|
nb_reports = Report.select().count()
|
||||||
|
nb_active_reports = Report.select().where(
|
||||||
|
(Report.expiration_datetime == None) |
|
||||||
|
(Report.expiration_datetime > arrow.utcnow().replace(microsecond=0).datetime)
|
||||||
|
).count()
|
||||||
|
last_added_report_datetime = Report.select().order_by(
|
||||||
|
Report.datetime.desc()
|
||||||
|
).get().datetime
|
||||||
|
|
||||||
|
return {
|
||||||
|
"data": {
|
||||||
|
"nb_reports": nb_reports,
|
||||||
|
"nb_active_reports": nb_active_reports,
|
||||||
|
"last_added_report_datetime": last_added_report_datetime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -36,6 +36,16 @@ export function getActiveReports() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getStats() {
|
||||||
|
return fetch(`${BASE_URL}api/v1/stats`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(response => response.data)
|
||||||
|
.catch((exc) => {
|
||||||
|
console.error(`Unable to fetch stats: ${exc}.`);
|
||||||
|
throw exc;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function downvote(id) {
|
export function downvote(id) {
|
||||||
return fetch(`${BASE_URL}api/v1/reports/${id}/downvote`, {
|
return fetch(`${BASE_URL}api/v1/reports/${id}/downvote`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
{{ report.label }}
|
{{ report.label }}
|
||||||
</v-flex>
|
</v-flex>
|
||||||
<v-flex xs12 class="secondLine">
|
<v-flex xs12 class="secondLine">
|
||||||
{{ $t('reportCard.Reported') }} {{ report.fromNow }}
|
{{ $t('reportCard.Reported', { fromNow: report.fromNow }) }}
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
{
|
{
|
||||||
"about": {
|
"about": {
|
||||||
"availableReportsTitle": "The available reports so far are:",
|
"availableReportsTitle": "The available reports so far are:",
|
||||||
"license": "It is released under an <a href=\"https://opensource.org/licenses/MIT\">MIT license</a> (<a href=\"https://framagit.org/phyks/cyclassist\">source code</a>). Icons are based on creations from Wikimedia, Vecteezy, Pixabay or Flaticon. Sounds are based on CC0 works from <a href=\"https://freesound.org/\">freesound.org</a>. The map background is using tiles from <a href=\"https://carto.com/location-data-services/basemaps/\">Carto.com</a> or <a href=\"http://thunderforest.com/\">Thunderforest</a>, thanks to <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap contributors</a> and <a href=\"http://leafletjs.com/\">Leaflet</a>. Collected reports are available under <a href=\"https://opendatacommons.org/licenses/odbl/\">ODbL license</a>. Manual location picking uses the awesome API from <a href=\"https://adresse.data.gouv.fr\">adresse.data.gouv.fr</a>.",
|
"lastReportAdded": "Last report added {fromNow}.",
|
||||||
|
"license": "License",
|
||||||
|
"licenseDescription": "It is released under an <a href=\"https://opensource.org/licenses/MIT\">MIT license</a> (<a href=\"https://framagit.org/phyks/cyclassist\">source code</a>). Icons are based on creations from Wikimedia, Vecteezy, Pixabay or Flaticon. Sounds are based on CC0 works from <a href=\"https://freesound.org/\">freesound.org</a>. The map background is using tiles from <a href=\"https://carto.com/location-data-services/basemaps/\">Carto.com</a> or <a href=\"http://thunderforest.com/\">Thunderforest</a>, thanks to <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap contributors</a> and <a href=\"http://leafletjs.com/\">Leaflet</a>. Collected reports are available under <a href=\"https://opendatacommons.org/licenses/odbl/\">ODbL license</a>. Manual location picking uses the awesome API from <a href=\"https://adresse.data.gouv.fr\">adresse.data.gouv.fr</a>.",
|
||||||
|
"nbActiveReports": "No active report. | One active report. | {nbActiveReports} active reports.",
|
||||||
|
"nbReports": "No report. | One report. | {nbReports} reports.",
|
||||||
|
"stats": "Stats",
|
||||||
"summary": "This app lets you track and share issues with bike lanes.",
|
"summary": "This app lets you track and share issues with bike lanes.",
|
||||||
"usage": "How to use",
|
"usage": "How to use",
|
||||||
"usageDescription": "Use the button in the lower right corner to add a new report at your current location. To add a report elsewhere, do a click where you want the report to be shown. Press on a marker on the map to display more informations and report the problem as being still there or solved."
|
"usageDescription": "Use the button in the lower right corner to add a new report at your current location. To add a report elsewhere, do a click where you want the report to be shown. Press on a marker on the map to display more informations and report the problem as being still there or solved."
|
||||||
@ -61,7 +66,7 @@
|
|||||||
"vibrate": "Vibrate"
|
"vibrate": "Vibrate"
|
||||||
},
|
},
|
||||||
"reportCard": {
|
"reportCard": {
|
||||||
"Reported": "Reported"
|
"Reported": "Reported {fromNow}."
|
||||||
},
|
},
|
||||||
"reportDialog": {
|
"reportDialog": {
|
||||||
"unableToSendDescription": "There was a network issue preventing from sending the latest report.",
|
"unableToSendDescription": "There was a network issue preventing from sending the latest report.",
|
||||||
|
@ -10,18 +10,52 @@
|
|||||||
<h2 class="body-2">{{ $t('about.availableReportsTitle') }}</h2>
|
<h2 class="body-2">{{ $t('about.availableReportsTitle') }}</h2>
|
||||||
<ReportsDescription></ReportsDescription>
|
<ReportsDescription></ReportsDescription>
|
||||||
|
|
||||||
<p class="mt-3" v-html="$t('about.license')"></p>
|
<h2 class="body-2 mt-3">{{ $t('about.stats') }}</h2>
|
||||||
|
<v-progress-circular indeterminate v-if="!stats"></v-progress-circular>
|
||||||
|
<ul v-else>
|
||||||
|
<li>{{ $tc('about.nbActiveReports', stats.nb_active_reports, { nbActiveReports: stats.nb_active_reports }) }}</li>
|
||||||
|
<li>{{ $tc('about.nbReports', stats.nb_reports, { nbReports: stats.nb_reports }) }}</li>
|
||||||
|
<li>{{ $t('about.lastReportAdded', { fromNow: stats.last_added_report_datetime }) }}</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 class="body-2 mt-3">{{ $t('about.license') }}</h2>
|
||||||
|
<p v-html="$t('about.licenseDescription')"></p>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
</v-layout>
|
</v-layout>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
import { getStats } from '@/api';
|
||||||
|
|
||||||
import ReportsDescription from '@/components/ReportsDescription.vue';
|
import ReportsDescription from '@/components/ReportsDescription.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ReportsDescription,
|
ReportsDescription,
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
stats: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
getStats().then((stats) => {
|
||||||
|
this.stats = stats;
|
||||||
|
this.stats.last_added_report_datetime = (
|
||||||
|
moment(this.stats.last_added_report_datetime).fromNow()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route: 'fetchData',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -12,6 +12,7 @@ import VGrid from 'vuetify/es5/components/VGrid';
|
|||||||
import VIcon from 'vuetify/es5/components/VIcon';
|
import VIcon from 'vuetify/es5/components/VIcon';
|
||||||
import VList from 'vuetify/es5/components/VList';
|
import VList from 'vuetify/es5/components/VList';
|
||||||
import VMenu from 'vuetify/es5/components/VMenu';
|
import VMenu from 'vuetify/es5/components/VMenu';
|
||||||
|
import VProgressCircular from 'vuetify/es5/components/VProgressCircular';
|
||||||
import VProgressLinear from 'vuetify/es5/components/VProgressLinear';
|
import VProgressLinear from 'vuetify/es5/components/VProgressLinear';
|
||||||
import VSelect from 'vuetify/es5/components/VSelect';
|
import VSelect from 'vuetify/es5/components/VSelect';
|
||||||
import VSwitch from 'vuetify/es5/components/VSwitch';
|
import VSwitch from 'vuetify/es5/components/VSwitch';
|
||||||
@ -32,6 +33,7 @@ Vue.use(Vuetify, {
|
|||||||
VIcon,
|
VIcon,
|
||||||
VList,
|
VList,
|
||||||
VMenu,
|
VMenu,
|
||||||
|
VProgressCircular,
|
||||||
VProgressLinear,
|
VProgressLinear,
|
||||||
VSelect,
|
VSelect,
|
||||||
VSwitch,
|
VSwitch,
|
||||||
|
Loading…
Reference in New Issue
Block a user