-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
474 etq visiteur personne non connectée je peux récupérer un catalogu…
…e de donnée au format csv (#480) * Add CatalogExportView and CSV implementation * WIP: handler * WIP: finish handler, need conversion * WIP: finish csv export + tests * Finish test, refactor * add cache * Address feedback Co-authored-by: florimondmanca <[email protected]>
- Loading branch information
1 parent
bd87b6f
commit e5fb5ec
Showing
12 changed files
with
392 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import csv | ||
import io | ||
|
||
from server.application.catalogs.views import CatalogExportView | ||
|
||
|
||
def to_csv(export: CatalogExportView) -> str: | ||
fieldnames = [ | ||
"titre", | ||
"description", | ||
"service", | ||
"couv_geo", | ||
"format", | ||
"si", | ||
"contact_service", | ||
"contact_personne", | ||
"freq_maj", | ||
"date_maj", | ||
"url", | ||
"licence", | ||
"mots_cles", | ||
] | ||
|
||
fieldnames.extend(extra_field.name for extra_field in export.catalog.extra_fields) | ||
|
||
f = io.StringIO() | ||
writer = csv.DictWriter(f, fieldnames=fieldnames) | ||
writer.writeheader() | ||
|
||
for dataset in export.datasets: | ||
row = { | ||
"titre": dataset.title, | ||
"description": dataset.description, | ||
"service": dataset.service, | ||
"couv_geo": dataset.geographical_coverage, | ||
"format": ", ".join(fmt.value for fmt in dataset.formats), | ||
"si": dataset.technical_source or "", | ||
"contact_service": dataset.producer_email or "", | ||
"contact_personne": ", ".join(dataset.contact_emails), | ||
"freq_maj": ( | ||
freq.value if (freq := dataset.update_frequency) is not None else "" | ||
), | ||
"date_maj": ( | ||
d.strftime("%d/%m/%Y") | ||
if (d := dataset.last_updated_at) is not None | ||
else "" | ||
), | ||
"url": dataset.url or "", | ||
"licence": dataset.license or "", | ||
"mots_cles": ", ".join(tag.name for tag in dataset.tags), | ||
} | ||
|
||
extra_field_value_by_id = { | ||
extra_field_value.extra_field_id: extra_field_value.value | ||
for extra_field_value in dataset.extra_field_values | ||
} | ||
|
||
for extra_field in export.catalog.extra_fields: | ||
row[extra_field.name] = extra_field_value_by_id.get(extra_field.id, "") | ||
|
||
writer.writerow(row) | ||
|
||
return f.getvalue() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import datetime as dt | ||
from typing import Callable, Dict, Optional, Tuple | ||
|
||
from server.domain.common.datetime import now | ||
from server.domain.organizations.types import Siret | ||
|
||
|
||
class ExportCache: | ||
""" | ||
Implement two types of cache to reduce the load associated to exporting catalogs: | ||
* Client-side caching, by adding 'Cache-Control' headers. | ||
Individual clients will only make new requests when their cache entry has expired. | ||
* Server-side caching, by storing exports in memory and reusing them for new | ||
clients until we consider them as stale (configurable). | ||
""" | ||
|
||
def __init__( | ||
self, max_age: dt.timedelta, nowfunc: Callable[[], dt.datetime] = now | ||
) -> None: | ||
self._exports: Dict[str, Tuple[dt.datetime, str]] = {} | ||
self._max_age = max_age | ||
self._cache_control = f"max-age={int(self._max_age.total_seconds())}" | ||
self._now = nowfunc | ||
|
||
def get(self, siret: Siret) -> Optional[str]: | ||
try: | ||
expiry_date, content = self._exports[siret] | ||
except KeyError: | ||
return None | ||
|
||
is_stale = self._now() > expiry_date | ||
|
||
if is_stale: | ||
del self._exports[siret] | ||
return None | ||
|
||
return content | ||
|
||
def set(self, siret: Siret, content: str) -> None: | ||
self._exports[siret] = (self._now() + self._max_age, content) | ||
|
||
@property | ||
def hit_headers(self) -> dict: | ||
return {"Cache-Control": self._cache_control, "X-Cache": "HIT"} | ||
|
||
@property | ||
def miss_headers(self) -> dict: | ||
return {"Cache-Control": self._cache_control} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.