Compare commits

..

10 Commits

12 changed files with 142 additions and 22 deletions

80
README.md Normal file
View File

@@ -0,0 +1,80 @@
graphPaname
===========
graphPaname is a system that collects real-time data, relevant to the
COVID-19 pandemic de-escalation, from the city of Paris.
It works with 4 datasets about the de-escalation:
- Retailers with home delivery
- Additional parking places in relay parkings (parkings connected to
public transportation)
- Temporary cycling paths
- Temporary pedestrian streets
For each dataset, we offer a table with the data, and a map of Paris
with markers. Additionally, there\'s a section with photos related to
the COVID-19 pandemic.
Technologies
------------
- Flask
- Pandas
- Folium
Data sources
------------
- [Open Data](https://opendata.paris.fr/pages/home/)
- [OpenStreetMap](https://www.openstreetmap.org/)
- [Flickr](https://flickr.com)
Requirements
------------
- Nix
Installation
------------
1. Install Nix (compatible with MacOS and Linux):
``` {.shell}
curl -L https://nixos.org/nix/install | sh
```
There are alternative installation methods, if you don\'t want to pipe
curl to sh
2. Clone the repository:
``` {.shell}
git clone https://coolneng.duckdns.org/gitea/coolneng/graphPaname
```
3. Change the working directory to the project:
``` {.shell}
cd graphPaname
```
4. Enter the nix-shell:
``` {.shell}
nix-shell
```
5. Run the tests:
``` {.shell}
pytest
```
6. Execute the Flask application:
``` {.shell}
flask run
```
The website can be accessed via **localhost:5000**

View File

@@ -1,4 +0,0 @@
* graphPaname
This project aims to gather information about the smart city of Paris and
organize it in different plots and tables.

View File

@@ -5,6 +5,7 @@ from flask_bootstrap import Bootstrap
app = Flask(__name__)
app.secret_key = SECRET_KEY
app.config['TEMPLATES_AUTO_RELOAD'] = True
bootstrap = Bootstrap(app)
from app import errors, routes

View File

@@ -4,5 +4,9 @@ from wtforms import SelectField, SubmitField
class DatasetForm(FlaskForm):
"""
Web form to select a dataset
"""
dataset = SelectField(choices=CHOICES)
submit = SubmitField("Submit")

View File

@@ -1,4 +1,4 @@
from folium import Map, Marker
from folium import Map, Marker, PolyLine
from pandas import DataFrame, json_normalize
from app.data_request import request_dataset
@@ -15,12 +15,24 @@ def create_dataframe(dataset) -> DataFrame:
return filtered_df
def reverse_coordinates(row):
"""
Reverses each tuples coordinates to ensure folium can parse them correctly
"""
coord = [tuple(reversed(t)) for t in row]
return coord
def create_map(df):
"""
Creates a Map with markers from the DataFrame
Creates a Map with markers or lines from the DataFrame
"""
m = Map(location=COORDINATES, zoom_start=12)
m = Map(location=COORDINATES, zoom_start=12, tiles="Stamen Terrain")
for index, row in df.iterrows():
if row["fields.geo_shape.type"] == "LineString":
coord = reverse_coordinates(row["fields.geo_shape.coordinates"])
PolyLine(locations=coord, color="blue", opacity=0.5).add_to(m)
else:
lng, lat = row["fields.geo_shape.coordinates"]
Marker(location=[lat, lng]).add_to(m)
m.save("app/templates/map.html")

View File

@@ -2,12 +2,18 @@ from app.preprocessing import create_dataframe, create_map
def create_table(df) -> str:
"""
Renders an HTML table from a DataFrame
"""
df.fillna(value=0, inplace=True)
table = df.to_html(classes=["table-striped", "table-sm", "table-responsive"])
return table
def process_data(dataset):
"""
Creates the DataFrame, produces a map and returns a table
"""
df = create_dataframe(dataset)
table = create_table(df)
create_map(df)

View File

@@ -28,7 +28,7 @@ def visualization():
@app.route("/map")
def map():
return render_template("map.html", title="Map")
return render_template("map.html")
@app.route("/photos")

View File

@@ -3,6 +3,24 @@
{% block content %}
<div class="jumbotron">
<h1 id="graphPaname">graphPaname</h1>
<p>graphPaname is an information system that aims to show real-time data, related to the COVID-19 outbreak, in the city of Paris</p>
<p>
graphPaname is a system that collects real-time data, relevant to the COVID-19 pandemic de-escalation, from the city of Paris.
</p>
<p>
It works with 4 datasets about the de-escalation:
</p>
<ul class="org-ul">
<li>Retailers with home delivery</li>
<li>Additional parking places in relay parkings (parkings connected to public transportation)</li>
<li>Temporary cycling paths</li>
<li>Temporary pedestrian streets</li>
</ul>
<p>
For each dataset, we offer a table with the data, and a map of Paris with markers. Additionally, there&rsquo;s a section with photos related to the COVID-19 pandemic.
</p>
</div>
{% endblock %}

View File

@@ -8,7 +8,7 @@
{{ table|safe }}
</div>
<div class="col-md-1">
<iframe class="map", src="/map" width="350" height="350"></iframe>
<iframe id="map", src="/map" width="350" height="350"></iframe>
</div>
</div>
<p><a href="{{ url_for('data') }}">Back</a></p>

View File

@@ -8,6 +8,7 @@ DATASET_URL = "https://opendata.paris.fr/api/records/1.0/search/?dataset={}&q=&r
FLICKR_URL = "https://www.flickr.com/search/?text={}"
COLUMNS = {
"deconfinement-pistes-cyclables-temporaires": [
"fields.geo_shape.type",
"fields.geo_shape.coordinates",
"fields.statut",
"record_timestamp",
@@ -17,12 +18,14 @@ COLUMNS = {
"fields.societe",
"fields.nb_places_dispositif_environ",
"fields.parcs",
"fields.geo_shape.type",
"fields.geo_shape.coordinates",
"fields.cp",
"fields.ville",
"fields.adresse",
],
"coronavirus-commercants-parisiens-livraison-a-domicile": [
"fields.geo_shape.type",
"fields.geo_shape.coordinates",
"fields.adresse",
"fields.code_postal",
@@ -35,6 +38,7 @@ COLUMNS = {
"fields.mail",
],
"deconfinement-rues-amenagees-pour-pietons": [
"fields.geo_shape.type",
"fields.geo_shape.coordinates",
"fields.nom_voie",
"fields.categorie",

View File

@@ -10,17 +10,8 @@ pkgs.mkShell {
flask
flask-bootstrap
flask_wtf
matplotlib
folium
pytest
beautifulsoup4
# Development tools
black
isort
pyflakes
python-language-server
pyls-black
pyls-isort
pyls-mypy
];
}

View File

@@ -3,7 +3,7 @@ from requests import get
from app.preprocessing import create_dataframe
from app.data_request import request_dataset
from constants import COLUMNS, DATASETS, DATASET_URL
from constants import COLUMNS, DATASETS, DATASET_URL, FLICKR_URL
def test_dataset_request():
@@ -23,3 +23,11 @@ def test_dataframe_creation():
df = create_dataframe(dataset)
assert isinstance(df, DataFrame)
assert all(df.columns == COLUMNS[dataset])
def test_flickr_request():
"""
Checks that Flickr search is avalaible
"""
response = get(FLICKR_URL.format("paris coronavirus"))
assert response.status_code == 200