Skip to content
Snippets Groups Projects
Commit 63fa4023 authored by Antoine Gaudron-Desjardins's avatar Antoine Gaudron-Desjardins
Browse files

Merge branch 'extract-stats' into 'main'

Extract stats

See merge request !23
parents 261e066c 5a680ddc
No related branches found
No related tags found
1 merge request!23Extract stats
Pipeline #43850 passed
......@@ -75,6 +75,7 @@ install-npm-packages:
lint-back:
stage: test
allow_failure: true
before_script:
- source venv/bin/activate
- pip install pycodestyle
......@@ -87,6 +88,7 @@ lint-back:
lint-front:
image: node:14.6.0
stage: test
allow_failure: true
script:
- cd frontend/
- npm run lint
......
"""
Module to interact with the database
"""
from datetime import date, datetime, time, timedelta
from numpy import average
from sqlalchemy.orm import Session
from sqlalchemy.sql import func
from db import models, schemas
......@@ -22,5 +26,49 @@ def create_record(new_record: schemas.RecordBase, db: Session):
def get_waiting_time(place: str, db: Session):
""" Get the last estimated waiting time for the given place """
db_record = db.query(models.Records).filter(models.Records.place == place).order_by(models.Records.date.desc()).one()
db_record = db.query(models.Records).filter(models.Records.place == place).order_by(models.Records.date.desc()).first()
return db_record.waiting_time
def get_stats(place: str, weekday: int, min_time_hour: int, min_time_mn: int, max_time_hour: int, max_time_mn: int, interval: timedelta, db: Session):
""" Get the average waiting time for each interval between two time steps """
def shift_time(t: time, delta: timedelta):
return (datetime.combine(date(1, 1, 1), t) + delta).time()
def avg_time_query(start_time, end_time):
records = db.query(
(func.round(
func.avg(
3600 * func.extract('HOUR', models.Records.waiting_time) +
60 * func.extract('MINUTE', models.Records.waiting_time) +
func.extract('SECOND', models.Records.waiting_time))
)) / 60
).filter(
models.Records.place == place,
func.weekday(models.Records.date) == weekday,
(func.extract('HOUR', models.Records.date) > start_time.hour) |
((func.extract('HOUR', models.Records.date) == start_time.hour) &
(func.extract('MINUTE', models.Records.date) >= start_time.minute)),
(func.extract('HOUR', models.Records.date) < end_time.hour) |
((func.extract('HOUR', models.Records.date) == end_time.hour) &
(func.extract('MINUTE', models.Records.date) < end_time.minute)),
).one()
if records[0]:
return int(records[0])
return None
def add_slot(slots_list, start_time, end_time):
average_waiting_time = avg_time_query(start_time, end_time)
if average_waiting_time:
name = f'{start_time.hour:02}h{start_time.minute:02}'
slots_list.append({'name': name, 'time': average_waiting_time})
min_time, max_time = time(min_time_hour, min_time_mn), time(max_time_hour, max_time_mn)
stats = []
start_time, end_time = min_time, shift_time(min_time, interval)
while start_time < max_time:
add_slot(stats, start_time, end_time)
start_time, end_time = end_time, shift_time(end_time, interval)
return stats
from datetime import timedelta
from datetime import datetime, time, timedelta
from typing import List
from fastapi import Body, Depends, FastAPI
from fastapi.middleware.cors import CORSMiddleware
......@@ -57,6 +57,12 @@ async def waiting_time(place: str, db: Session = Depends(get_db)):
return crud.get_waiting_time(place, db)
@app.get('/api/{place}/stats/{day}/{min_time_hour}/{min_time_mn}/{max_time_hour}/{max_time_mn}/{interval}', response_model=list)
async def stats(place: str, day: int, min_time_hour: int, min_time_mn: int,
max_time_hour: int, max_time_mn: int, interval: timedelta, db: Session = Depends(get_db)):
return crud.get_stats(place, day, min_time_hour, min_time_mn, max_time_hour, max_time_mn, interval, db)
"""
import cv2
import numpy as np
......
This diff is collapsed.
......@@ -15,7 +15,8 @@
"react-helmet": "^6.1.0",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"react-spring": "^9.4.5"
"react-spring": "^9.4.5",
"recharts": "^2.1.12"
},
"scripts": {
"start": "react-scripts start",
......@@ -25,21 +26,6 @@
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"eslint": "^8.19.0",
"eslint-config-prettier": "^8.5.0",
......
import React from "react";
import axios from "axios";
import {
AreaChart,
Area,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
} from "recharts";
import gradient from "./gradient";
import "../styles/Graph.css";
export default function DailyGraph({
place,
day,
min_time_hour,
min_time_mn,
max_time_hour,
max_time_mn,
interval,
}) {
const url =
process.env.REACT_APP_BASE_URL_BACK +
"/" +
place +
"/stats/" +
day +
"/" +
min_time_hour +
"/" +
min_time_mn +
"/" +
max_time_hour +
"/" +
max_time_mn +
"/" +
interval;
const [data, setData] = React.useState([]);
React.useEffect(() => {
axios.get(url).then((response) => {
setData(response.data);
console.log(response.data);
});
}, [url]);
if (!data) return null;
const CustomTooltip = ({ active, payload }) => {
if (active && payload && payload.length) {
return (
<div className="custom-tooltip">
<p className="label">{`Temps d'attente : ${payload[0].value} minutes`}</p>
</div>
);
}
return null;
};
return (
<div className="parent">
<div className="graph">
<ResponsiveContainer width="100%" height="100%">
<AreaChart
width={500}
height={300}
data={data}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
>
{gradient()}
<CartesianGrid stroke="#FFFFFF" strokeDasharray="1 5" />
<XAxis
axisLine={false}
tickLine={false}
tick={{ fill: "#FFFFFF", fontSize: "18" }}
padding={{ left: 20 }}
dataKey="name"
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fill: "#FFFFFF", fontSize: "18" }}
dataKey="time"
/>
<Tooltip content={<CustomTooltip />} />
<Area
type="monotone"
dataKey="time"
stroke="#5661B3"
fillOpacity={1}
fill="url(#colorGradient)"
dot={{ stroke: "#0967D2", strokeWidth: 2, fill: "#fff" }}
/>
</AreaChart>
</ResponsiveContainer>
</div>
</div>
);
}
......@@ -4,15 +4,18 @@ import axios from "axios";
import "../styles/WaitingTime.css";
export default function WaitingTime({ place }) {
const baseURL = process.env.REACT_APP_BASE_URL_BACK + "/" + place + "/waiting_time";
const url = process.env.REACT_APP_BASE_URL_BACK + "/" + place + "/waiting_time";
const [post, setPost] = React.useState(null);
React.useEffect(() => {
axios.get(baseURL).then((response) => {
axios.get(url).then((response) => {
if (response.data < 60) {
setPost(0);
} else {
setPost(Math.round(response.data / 60));
console.log(response.data);
}
});
}, [baseURL]);
}, [url]);
if (!post) return null;
return (
......
import React from "react";
export default function gradient() {
return (
<defs>
<linearGradient id="colorGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="10%" stopColor="#ff0000" stopOpacity={0.55} />
<stop offset="50%" stopColor="#fff200" stopOpacity={0.55} />
<stop offset="90%" stopColor="#1e9600" stopOpacity={0.55} />
</linearGradient>
</defs>
);
}
......@@ -2,3 +2,4 @@ export { default as Header } from "./Header";
export { default as Footer } from "./Footer";
export { default as Timetable } from "./Timetable";
export { default as WaitingTime } from "./WaitingTime";
export { default as DailyGraph } from "./Graph";
.footer{
position: fixed;
bottom: 0;
width: 100%;
}
......
.parent{
display: flex;
justify-content: center;
}
.graph{
height: 30em;
width: 60em;
display: flex;
}
\ No newline at end of file
body {
height: 100%;
body, html {
height: 100vh;
color: white;
text-align: center;
background: linear-gradient(to right, #B06AB3, #4568DC);
min-height: 100%;
}
import React from "react";
import Timetable from "../components/Timetable";
import WaitingTime from "../components/WaitingTime";
import { DailyGraph, Timetable, WaitingTime } from "../components";
export default function Eiffel() {
return (
<div>
<h2>RU Eiffel</h2>
<WaitingTime place="eiffel" />
<DailyGraph
place="eiffel"
day={3}
min_time_hour={12}
min_time_mn={0}
max_time_hour={12}
max_time_mn={40}
interval={300}
/>
<Timetable
schedule={{
LundiMidi: "11h30 - 14h",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment