|
1 | 1 | """tifeatures.factory: router factories.""" |
2 | 2 |
|
| 3 | +import csv |
3 | 4 | import json |
4 | 5 | from dataclasses import dataclass, field |
5 | | -from typing import Any, Callable, List, Optional |
| 6 | +from typing import Any, Callable, Dict, Iterable, List, Optional |
6 | 7 |
|
7 | 8 | import jinja2 |
8 | 9 | from pygeofilter.ast import AstType |
|
38 | 39 | from starlette.responses import StreamingResponse |
39 | 40 | from starlette.templating import Jinja2Templates, _TemplateResponse |
40 | 41 |
|
| 42 | + |
| 43 | +class DummyWriter: |
| 44 | + """Dummy writer that implements write for use with csv.writer.""" |
| 45 | + |
| 46 | + def write(self, line: str): |
| 47 | + """Return line.""" |
| 48 | + return line |
| 49 | + |
| 50 | + |
| 51 | +def iter_csv(data: Iterable[Dict]): |
| 52 | + """Creates an iterator that returns lines of csv from an iterable of dicts.""" |
| 53 | + |
| 54 | + initial = True |
| 55 | + writer = None |
| 56 | + for row in data: |
| 57 | + if initial: |
| 58 | + fieldnames = row.keys() |
| 59 | + writer = csv.DictWriter(DummyWriter(), fieldnames=fieldnames) |
| 60 | + yield writer.writeheader() |
| 61 | + initial = False |
| 62 | + if writer: |
| 63 | + yield writer.writerow(row) |
| 64 | + |
| 65 | + |
41 | 66 | settings = APISettings() |
42 | 67 |
|
43 | 68 | # custom template directory |
@@ -591,41 +616,54 @@ async def items( |
591 | 616 | simplify=simplify, |
592 | 617 | ) |
593 | 618 |
|
594 | | - # CSV Response |
595 | | - if output_type == MediaType.csv: |
596 | | - props = list(items[0].properties.keys()) + ["geometry"] if items else [] |
597 | | - rows = [";".join(["collectionId", "itemId", *props]) + "\n"] |
598 | | - for f in items: |
599 | | - rows.append( |
600 | | - ";".join( |
601 | | - list( |
602 | | - map( |
603 | | - str, |
604 | | - [ |
605 | | - collection.id, |
606 | | - f.id, |
607 | | - *f.properties.values(), |
608 | | - f.geometry.wkt, |
609 | | - ], |
610 | | - ) |
611 | | - ) |
612 | | - ) |
613 | | - + "\n" |
| 619 | + if output_type in ( |
| 620 | + MediaType.csv, |
| 621 | + MediaType.json, |
| 622 | + MediaType.ndjson, |
| 623 | + ): |
| 624 | + if items[0].geometry is not None: |
| 625 | + rows = ( |
| 626 | + { |
| 627 | + "collectionId": collection.id, |
| 628 | + "itemId": f.id, |
| 629 | + **f.properties, |
| 630 | + "geometry": f.geometry.wkt, |
| 631 | + } |
| 632 | + for f in items |
614 | 633 | ) |
615 | | - return StreamingResponse( |
616 | | - iter(rows), |
617 | | - media_type=MediaType.csv, |
618 | | - headers={"Content-Disposition": "attachment;filename=items.csv"}, |
619 | | - ) |
620 | | - |
621 | | - # JSON Response |
622 | | - if output_type == MediaType.json: |
623 | | - return JSONResponse( |
624 | | - [ |
625 | | - {"colectionId": collection.id, "itemId": f.id, **f.properties} |
| 634 | + else: |
| 635 | + rows = ( |
| 636 | + { |
| 637 | + "collectionId": collection.id, |
| 638 | + "itemId": f.id, |
| 639 | + **f.properties, |
| 640 | + } |
626 | 641 | for f in items |
627 | | - ] |
628 | | - ) |
| 642 | + ) |
| 643 | + |
| 644 | + # CSV Response |
| 645 | + if output_type == MediaType.csv: |
| 646 | + return StreamingResponse( |
| 647 | + iter_csv(rows), |
| 648 | + media_type=MediaType.csv, |
| 649 | + headers={ |
| 650 | + "Content-Disposition": "attachment;filename=items.csv" |
| 651 | + }, |
| 652 | + ) |
| 653 | + |
| 654 | + # JSON Response |
| 655 | + if output_type == MediaType.json: |
| 656 | + return JSONResponse([row for row in rows]) |
| 657 | + |
| 658 | + # NDJSON Response |
| 659 | + if output_type == MediaType.ndjson: |
| 660 | + return StreamingResponse( |
| 661 | + (row + "\n" for row in rows), |
| 662 | + media_type=MediaType.ndjson, |
| 663 | + headers={ |
| 664 | + "Content-Disposition": "attachment;filename=items.ndjson" |
| 665 | + }, |
| 666 | + ) |
629 | 667 |
|
630 | 668 | qs = "?" + str(request.query_params) if request.query_params else "" |
631 | 669 | links = [ |
@@ -659,7 +697,10 @@ async def items( |
659 | 697 | ) |
660 | 698 | links.append( |
661 | 699 | model.Link( |
662 | | - href=url, rel="next", type=MediaType.geojson, title="Next page" |
| 700 | + href=url, |
| 701 | + rel="next", |
| 702 | + type=MediaType.geojson, |
| 703 | + title="Next page", |
663 | 704 | ), |
664 | 705 | ) |
665 | 706 |
|
|
0 commit comments