cetk 0.0.4.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. cetk/__init__.py +87 -0
  2. cetk/db.py +41 -0
  3. cetk/edb/__init__.py +0 -0
  4. cetk/edb/apps.py +9 -0
  5. cetk/edb/cache.py +249 -0
  6. cetk/edb/const.py +26 -0
  7. cetk/edb/copy.py +18 -0
  8. cetk/edb/exporters.py +551 -0
  9. cetk/edb/importers/__init__.py +36 -0
  10. cetk/edb/importers/activity_import.py +279 -0
  11. cetk/edb/importers/codeset_import.py +124 -0
  12. cetk/edb/importers/gridsource_import.py +256 -0
  13. cetk/edb/importers/roadsource_import.py +1602 -0
  14. cetk/edb/importers/source_import.py +958 -0
  15. cetk/edb/importers/timevar_import.py +115 -0
  16. cetk/edb/importers/utils.py +92 -0
  17. cetk/edb/importers/validation.py +207 -0
  18. cetk/edb/ltreefield.py +76 -0
  19. cetk/edb/migrations/0001_initial.py +1731 -0
  20. cetk/edb/migrations/0002_data.py +156 -0
  21. cetk/edb/migrations/0003_create_gpkg_tables.py +14 -0
  22. cetk/edb/migrations/__init__.py +0 -0
  23. cetk/edb/models/__init__.py +49 -0
  24. cetk/edb/models/base.py +52 -0
  25. cetk/edb/models/common_models.py +144 -0
  26. cetk/edb/models/fleets.py +160 -0
  27. cetk/edb/models/gridsource_models.py +163 -0
  28. cetk/edb/models/road_classes.py +197 -0
  29. cetk/edb/models/road_models.py +413 -0
  30. cetk/edb/models/source_models.py +399 -0
  31. cetk/edb/models/timevar_models.py +111 -0
  32. cetk/edb/rasterize/__init__.py +1 -0
  33. cetk/edb/rasterize/rasterizer.py +1544 -0
  34. cetk/edb/signals.py +43 -0
  35. cetk/edb/traffic.py +75 -0
  36. cetk/edb/units.py +160 -0
  37. cetk/emissions/__init__.py +0 -0
  38. cetk/emissions/aggregate_emissions.sql +303 -0
  39. cetk/emissions/areasource_emissions.sql +53 -0
  40. cetk/emissions/calc.py +148 -0
  41. cetk/emissions/filters.py +179 -0
  42. cetk/emissions/gridsource_emissions.sql +52 -0
  43. cetk/emissions/pointsource_emissions.sql +53 -0
  44. cetk/emissions/queries.py +221 -0
  45. cetk/emissions/roadsource_emissions.sql +211 -0
  46. cetk/emissions/used_substances.sql +56 -0
  47. cetk/emissions/views.py +89 -0
  48. cetk/logging.py +68 -0
  49. cetk/settings.py +85 -0
  50. cetk/tools/__init__.py +1 -0
  51. cetk/tools/cetk_command.py +599 -0
  52. cetk/tools/manage.py +24 -0
  53. cetk/tools/utils.py +334 -0
  54. cetk/utils.py +62 -0
  55. cetk-0.0.4.dev0.dist-info/LICENSE +201 -0
  56. cetk-0.0.4.dev0.dist-info/METADATA +297 -0
  57. cetk-0.0.4.dev0.dist-info/RECORD +60 -0
  58. cetk-0.0.4.dev0.dist-info/WHEEL +5 -0
  59. cetk-0.0.4.dev0.dist-info/entry_points.txt +3 -0
  60. cetk-0.0.4.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,599 @@
1
+ """Command line interface for managing a Clair emission inventory offline."""
2
+
3
+ import argparse
4
+ import datetime
5
+ import os
6
+ import sys
7
+ from math import ceil
8
+ from pathlib import Path
9
+
10
+ from django.core import serializers
11
+ from openpyxl import load_workbook
12
+ from pyproj import CRS
13
+ from pyproj.exceptions import CRSError
14
+
15
+ import cetk
16
+ from cetk import logging
17
+ from cetk.db import run_migrate
18
+ from cetk.tools.utils import (
19
+ CalledProcessError,
20
+ SubprocessError,
21
+ add_standard_command_options,
22
+ check_and_get_path,
23
+ create_from_template,
24
+ get_db,
25
+ get_template_db,
26
+ )
27
+
28
+ log = logging.getLogger("cetk")
29
+
30
+ settings = cetk.configure()
31
+
32
+ from cetk.edb.const import DEFAULT_SRID # noqa
33
+ from cetk.edb.const import DEFAULT_EMISSION_UNIT, SHEET_NAMES, WGS84_SRID # noqa
34
+ from cetk.edb.exporters import export_sources # noqa
35
+ from cetk.edb.importers import import_activitycodesheet # noqa
36
+ from cetk.edb.importers import ( # noqa
37
+ import_gridsources,
38
+ import_sourceactivities,
39
+ import_traffic,
40
+ )
41
+ from cetk.edb.importers.utils import ValidationError # noqa
42
+ from cetk.edb.models import Settings, Substance # noqa
43
+ from cetk.edb.rasterize.rasterizer import EmissionRasterizer, Output # noqa
44
+ from cetk.emissions.calc import aggregate_emissions # noqa
45
+ from cetk.emissions.calc import get_used_substances # noqa
46
+ from cetk.emissions.views import create_emission_table # noqa
47
+
48
+ SOURCETYPES = ("point", "area", "grid", "road")
49
+
50
+ sheet_choices = ["All"]
51
+ sheet_choices.extend(SHEET_NAMES)
52
+
53
+
54
+ def adjust_extent(extent, srid, cellsize):
55
+ """adjust extent to include an integer nr of cells."""
56
+ x1, y1, x2, y2 = (
57
+ extent or Settings.get_current().extent.transform(srid, clone=True).extent
58
+ )
59
+ nx = ceil((x2 - x1) / cellsize)
60
+ ny = ceil((y2 - y1) / cellsize)
61
+ x2 = x1 + nx * cellsize
62
+ y2 = y1 + ny * cellsize
63
+ extent = (x1, y1, x2, y2)
64
+ # Settings.extent is a Polygon, Settings.extent.extent a tuple (x1, y1, x2, y2)
65
+ if extent is None:
66
+ log.error("could not rasterize emissions, default extent not set")
67
+ sys.exit(1)
68
+ return extent, ny, nx
69
+
70
+
71
+ class Editor(object):
72
+ def __init__(self):
73
+ self.db_path = settings.DATABASES["default"]["NAME"]
74
+
75
+ def migrate(self, template=False, db_path=None):
76
+ if db_path is None:
77
+ if template:
78
+ db_path = get_template_db()
79
+ else:
80
+ db_path = get_db()
81
+ log.debug(f"Running migrations for database {db_path}")
82
+ try:
83
+ std_out, std_err = run_migrate(db_path=db_path)
84
+ except (CalledProcessError, SubprocessError) as err:
85
+ log.error(f"Error while migrating {db_path}: {err}")
86
+ else:
87
+ log.debug(f"Successfully migrated database {db_path}")
88
+
89
+ def info(self, **kwargs):
90
+ settings = Settings.get_current()
91
+ print(serializers.serialize("json", [settings]))
92
+
93
+ def update_settings(self, srid=None, codeset1=None, codeset2=None, codeset3=None):
94
+ from cetk.edb.models import CodeSet, Settings
95
+
96
+ settings = Settings.get_current()
97
+ if srid == WGS84_SRID:
98
+ log.error(
99
+ "SRID in settings must be a "
100
+ "projected coordinate system in meters, not lat/lon.\n"
101
+ )
102
+ sys.exit(1)
103
+
104
+ elif srid is not None:
105
+ try:
106
+ crs = CRS.from_epsg(srid) # noqa
107
+ except CRSError:
108
+ log.error(f"SRID={srid} does not define a valid CRS.\n")
109
+ sys.exit(1)
110
+ settings.srid = srid
111
+ log.info(f"Updated srid to {srid}.\n")
112
+ try:
113
+ if codeset1 is not None:
114
+ slug = codeset1
115
+ settings.codeset1 = CodeSet.objects.get(slug=slug)
116
+ log.info(f"Updated code-set1 to {slug}.\n")
117
+ if codeset2 is not None:
118
+ slug = codeset2
119
+ settings.codeset2 = CodeSet.objects.get(slug=slug)
120
+ log.info(f"Updated code-set2 to {slug}.\n")
121
+ if codeset3 is not None:
122
+ slug = codeset3
123
+ settings.codeset3 = CodeSet.objects.get(slug=slug)
124
+ log.info(f"Updated code-set3 to {slug}.\n")
125
+ except CodeSet.DoesNotExist:
126
+ log.error(f"No code-set defined with slug: '{slug}'")
127
+ sys.exit(1)
128
+ settings.save()
129
+
130
+ def import_workbook(self, filename, sheets=SHEET_NAMES, dry_run=False):
131
+ return_msg = []
132
+ db_updates = {}
133
+ workbook = load_workbook(filename=filename, data_only=True, read_only=True)
134
+ import_sheets = [
135
+ s
136
+ for s in SHEET_NAMES
137
+ if s in frozenset(sheets) and s in frozenset(workbook.sheetnames)
138
+ ]
139
+
140
+ try:
141
+ missing_sheets = set(import_sheets).difference(workbook.sheetnames)
142
+ if len(missing_sheets) > 0:
143
+ log.info(f"Workbook has no sheets named: {missing_sheets}")
144
+
145
+ # PointSource and AreaSource
146
+ sourceact = [
147
+ "CodeSet",
148
+ "ActivityCode",
149
+ "EmissionFactor",
150
+ "Timevar",
151
+ "PointSource",
152
+ "AreaSource",
153
+ ]
154
+
155
+ if any(name in sourceact for name in import_sheets):
156
+ updates, msgs = import_sourceactivities(
157
+ filename,
158
+ import_sheets=import_sheets,
159
+ validation=dry_run,
160
+ )
161
+ db_updates.update(updates)
162
+ return_msg += msgs
163
+
164
+ traffic = [
165
+ "RoadSource",
166
+ "VehicleFuel",
167
+ "Fleet",
168
+ "CongestionProfile",
169
+ "FlowTimevar",
170
+ "ColdstartTimevar",
171
+ "RoadAttribute",
172
+ "TrafficSituation",
173
+ "VehicleEmissionFactor",
174
+ ]
175
+ if any(name in traffic for name in import_sheets):
176
+ updates, msgs = import_traffic(
177
+ filename, sheets=import_sheets, validation=dry_run
178
+ )
179
+ return_msg += msgs
180
+ db_updates.update(updates)
181
+ if "GridSource" in import_sheets:
182
+ updates, msgs = import_gridsources(filename)
183
+ db_updates.update(updates)
184
+ return_msg += msgs
185
+ # skip empty messages
186
+ # return_msg = [entry.strip() for entry in return_msg if entry]
187
+ if len(return_msg) > 0:
188
+ raise ValidationError()
189
+ except ValidationError as err:
190
+ return_msg.append(str(err))
191
+ if not dry_run:
192
+ run_type = "import"
193
+ else:
194
+ run_type = "validation"
195
+ if len(return_msg) > 10:
196
+ log.error(
197
+ f">10 errors during {run_type}, first 10:"
198
+ f"{os.linesep}{return_msg[:10]}"
199
+ )
200
+ else:
201
+ log.error(f"Errors during {run_type}:" f"{os.linesep}{return_msg}")
202
+ else:
203
+ log.info(f"getting here {datetime.datetime.now()}")
204
+ if not dry_run:
205
+ log.info(
206
+ f"{datetime.datetime.now()} successfully imported {db_updates}"
207
+ )
208
+ else:
209
+ log.info(
210
+ f"{datetime.datetime.now()} successfully validated {db_updates}"
211
+ )
212
+ workbook.close()
213
+ return db_updates, return_msg
214
+
215
+ def update_emission_tables(
216
+ self,
217
+ sourcetypes=None,
218
+ unit=DEFAULT_EMISSION_UNIT,
219
+ substances=None,
220
+ ):
221
+ sourcetypes = sourcetypes or ("point", "area", "road")
222
+ substances = substances or get_used_substances()
223
+ if len(substances) == 0:
224
+ log.error("No emission factors or direct emissions found in database")
225
+ sys.exit(1)
226
+
227
+ for sourcetype in sourcetypes:
228
+ create_emission_table(sourcetype, substances=substances, unit=unit)
229
+
230
+ def aggregate_emissions(
231
+ self,
232
+ filename,
233
+ sourcetypes=None,
234
+ unit=DEFAULT_EMISSION_UNIT,
235
+ codeset=None,
236
+ substances=None,
237
+ ):
238
+ df = aggregate_emissions(
239
+ sourcetypes=sourcetypes, unit=unit, codeset=codeset, substances=substances
240
+ )
241
+ try:
242
+ df.to_excel(filename)
243
+ except Exception as err:
244
+ log.error(
245
+ f"could not write aggregated emission to file {filename}: {str(err)}"
246
+ )
247
+ sys.exit(1)
248
+
249
+ def rasterize_emissions(
250
+ self,
251
+ outputpath,
252
+ cellsize,
253
+ sourcetypes=None,
254
+ point_ids=None,
255
+ area_ids=None,
256
+ grid_ids=None,
257
+ road_ids=None,
258
+ unit=DEFAULT_EMISSION_UNIT,
259
+ codeset=None,
260
+ substances=None,
261
+ begin=None,
262
+ end=None,
263
+ extent=None,
264
+ srid=None,
265
+ timezone=None,
266
+ ):
267
+ substances = substances or get_used_substances()
268
+ timezone = timezone or datetime.timezone.utc
269
+ srid = srid or DEFAULT_SRID
270
+ extent, ny, nx = adjust_extent(extent, srid, cellsize)
271
+ try:
272
+ output = Output(
273
+ extent=extent, timezone=timezone, path=outputpath, srid=srid
274
+ )
275
+ rasterizer = EmissionRasterizer(output, nx=nx, ny=ny)
276
+ rasterizer.process(
277
+ substances,
278
+ begin=begin,
279
+ end=end,
280
+ unit=unit,
281
+ sourcetypes=sourcetypes,
282
+ point_ids=point_ids,
283
+ area_ids=area_ids,
284
+ grid_ids=grid_ids,
285
+ road_ids=road_ids,
286
+ )
287
+ except Exception as err:
288
+ log.error(f"could not rasterize emissions: {str(err)}")
289
+ sys.exit(1)
290
+
291
+ def export_data(self, filename):
292
+ export_sources(filename)
293
+ return True
294
+
295
+
296
+ def main():
297
+ db_path = get_db() or "unspecified"
298
+ parser = argparse.ArgumentParser(
299
+ description="Manage Clair offline emission inventories",
300
+ usage=f"""cetk <command> [<args>]
301
+
302
+ Main commands are:
303
+ create create an sqlite inventory
304
+ migrate migrate an sqlite inventory
305
+ info print settings
306
+ import import data
307
+ export export data
308
+ calc calculate emissions
309
+ settings change database settings
310
+
311
+ Current database is {db_path} (set by $CETK_DATABASE_PATH)
312
+ """,
313
+ )
314
+ add_standard_command_options(parser)
315
+ parser.add_argument(
316
+ "command",
317
+ help="Subcommand to run",
318
+ choices=("migrate", "create", "info", "import", "export", "calc", "settings"),
319
+ )
320
+ verbosity = [arg for arg in sys.argv if arg == "-v"]
321
+ sys_args = [arg for arg in sys.argv if arg != "-v"]
322
+ sub_args = sys_args[2:]
323
+ main_args = parser.parse_args(args=sys_args[1:2] + verbosity)
324
+ editor = Editor()
325
+ if main_args.command == "create":
326
+ sub_parser = argparse.ArgumentParser(
327
+ description="Create database from template.",
328
+ usage="cetk create <filename>",
329
+ )
330
+ sub_parser.add_argument(
331
+ "filename",
332
+ help="Path of new database",
333
+ )
334
+ args = sub_parser.parse_args(sub_args)
335
+ create_from_template(args.filename)
336
+ log.debug(
337
+ f"Created new database '{args.filename}' "
338
+ f"from template '{get_template_db()}'"
339
+ )
340
+ sys.exit(0)
341
+
342
+ if (
343
+ len(sys.argv) < 2
344
+ and sys.argv[2] not in ("-h", "--help", "--version")
345
+ and db_path == "unspecified"
346
+ ):
347
+ sys.stderr.write("No database specified, set by $CETK_DATABASE_PATH\n")
348
+ sys.exit(1)
349
+
350
+ if main_args.command == "info":
351
+ sub_parser = argparse.ArgumentParser(
352
+ description="Print or update settings.",
353
+ usage="usage: cetk info ",
354
+ )
355
+ args = sub_parser.parse_args(sub_args)
356
+ editor.info()
357
+
358
+ if main_args.command == "migrate":
359
+ sub_parser = argparse.ArgumentParser(
360
+ description=f"Migrate database {db_path}.",
361
+ usage="usage: cetk migrate",
362
+ )
363
+ sub_parser.add_argument(
364
+ "--template",
365
+ action="store_true",
366
+ help="Migrate the template database",
367
+ )
368
+ sub_parser.add_argument(
369
+ "--dbpath",
370
+ help="Specify database path manually",
371
+ )
372
+ args = sub_parser.parse_args(sub_args)
373
+ editor.migrate(template=args.template, db_path=args.dbpath)
374
+ elif main_args.command == "import":
375
+ sub_parser = argparse.ArgumentParser(
376
+ description="Import data from an xlsx-file",
377
+ usage="cetk import <filename> [options]",
378
+ )
379
+ sub_parser.add_argument(
380
+ "filename", help="Path to xslx-file", type=check_and_get_path
381
+ )
382
+ sub_parser.add_argument(
383
+ "--sheets",
384
+ nargs="+",
385
+ default=SHEET_NAMES,
386
+ help=f"List of sheets to import, valid names {SHEET_NAMES}",
387
+ )
388
+ sub_parser.add_argument(
389
+ "--dryrun",
390
+ action="store_true",
391
+ help="Do dry run to validate import file without actually importing data",
392
+ )
393
+ args = sub_parser.parse_args(sub_args)
394
+ if not Path(db_path).exists():
395
+ sys.stderr.write(
396
+ f"Database {db_path} does not exist, first run "
397
+ "'cetk create' or 'cetk migrate'\n"
398
+ )
399
+ sys.exit(1)
400
+
401
+ editor.import_workbook(args.filename, sheets=args.sheets, dry_run=args.dryrun)
402
+ sys.exit(0)
403
+ elif main_args.command == "calc":
404
+ sub_parser = argparse.ArgumentParser(
405
+ description="Calculate emissions",
406
+ usage="cetk calc [options]",
407
+ )
408
+ sub_parser.add_argument(
409
+ "--unit",
410
+ default=DEFAULT_EMISSION_UNIT,
411
+ help="Unit of emissions, default=%(default)s",
412
+ )
413
+ sub_parser.add_argument(
414
+ "--sourcetypes", nargs="*", help="Only sourcetypes", choices=SOURCETYPES
415
+ )
416
+ sub_parser.add_argument(
417
+ "--substances",
418
+ nargs="*",
419
+ help="Only substances (default is all with emissions)",
420
+ choices=Substance.objects.values_list("slug", flat=True),
421
+ metavar=("NOx", "PM10"),
422
+ )
423
+ calc_grp = sub_parser.add_mutually_exclusive_group()
424
+ calc_grp.add_argument(
425
+ "--update", help="Create/update emission tables", action="store_true"
426
+ )
427
+ calc_grp.add_argument(
428
+ "--aggregate", help="Aggregate emissions", metavar="FILENAME"
429
+ )
430
+ aggregate_grp = sub_parser.add_argument_group(
431
+ "aggregate emissions", description="Options to aggregate emissions"
432
+ )
433
+ aggregate_grp.add_argument(
434
+ "--codeset", help="Aggregate emissions by codeset", metavar="SLUG"
435
+ )
436
+ calc_grp.add_argument(
437
+ "--rasterize", help="Rasterize emissions", metavar="OUTPUTPATH"
438
+ )
439
+ rasterize_grp = sub_parser.add_argument_group(
440
+ "rasterize emissions", description="Settings to rasterize emissions"
441
+ )
442
+ rasterize_grp.add_argument(
443
+ "--cellsize", help="Cellsize (meter) in output raster", type=float
444
+ )
445
+ rasterize_grp.add_argument(
446
+ "--extent",
447
+ help="Extent of output raster. Settings.extent is taken otherwise",
448
+ nargs=4,
449
+ type=float,
450
+ metavar=("x1", "y1", "x2", "y2"),
451
+ )
452
+ rasterize_grp.add_argument(
453
+ "--srid",
454
+ help="EPSG of output raster. 4-5 digits integer",
455
+ metavar="EPSG",
456
+ )
457
+ rasterize_grp.add_argument(
458
+ "--begin",
459
+ help="when hourly rasters are desired, specify begin date."
460
+ + " Time 00:00 assumed",
461
+ metavar="YYMMDDHH",
462
+ )
463
+ rasterize_grp.add_argument(
464
+ "--end",
465
+ help="when hourly rasters are desired, specify end date"
466
+ + " Time 00:00 assumed",
467
+ metavar="YYMMDDHH",
468
+ )
469
+ rasterize_grp.add_argument(
470
+ "--point-ids",
471
+ nargs="+",
472
+ help="List of pointsource id's to include in rasterizer. "
473
+ + "All sources will be rasterized for other sourcetypes if not limited "
474
+ + "using argument sourcetypes or filtering specified for ids for "
475
+ + " other sourcetypes too.",
476
+ )
477
+ rasterize_grp.add_argument(
478
+ "--area-ids",
479
+ nargs="+",
480
+ help="List of areasource id's to include in rasterizer",
481
+ )
482
+ rasterize_grp.add_argument(
483
+ "--road-ids",
484
+ nargs="+",
485
+ help="List of roadsource id's to include in rasterizer",
486
+ )
487
+ rasterize_grp.add_argument(
488
+ "--grid-ids",
489
+ nargs="+",
490
+ help="List of gridsource id's to include in rasterizer",
491
+ )
492
+ # TODO add argument begin/end for rasterize
493
+ # TODO add argument to aggregate emissions within polygon
494
+
495
+ args = sub_parser.parse_args(sub_args)
496
+ try:
497
+ if args.substances is not None:
498
+ substances = []
499
+ for s in args.substances:
500
+ substances.append(Substance.objects.get(slug=s))
501
+ else:
502
+ substances = None
503
+ except Substance.DoesNotExist:
504
+ sys.stderr.write(f"Substance {s} does not exist.\n")
505
+ if not Path(db_path).exists():
506
+ sys.stderr.write("Database does not exist.\n")
507
+ sys.exit(1)
508
+ if args.update:
509
+ editor.update_emission_tables(
510
+ sourcetypes=args.sourcetypes, unit=args.unit, substances=args.substances
511
+ )
512
+ sys.stdout.write("Successfully updated tables\n")
513
+ sys.exit(0)
514
+ if args.aggregate is not None:
515
+ editor.aggregate_emissions(
516
+ args.aggregate,
517
+ substances=substances,
518
+ sourcetypes=args.sourcetypes,
519
+ unit=args.unit,
520
+ codeset=args.codeset,
521
+ )
522
+ sys.stdout.write("Successfully aggregated emissions\n")
523
+ sys.exit(0)
524
+ if args.rasterize is not None:
525
+ if args.begin is not None:
526
+ args.begin = datetime.datetime.strptime(args.begin, "%y%m%d%H").replace(
527
+ tzinfo=datetime.timezone.utc
528
+ )
529
+ if args.end is not None:
530
+ args.end = datetime.datetime.strptime(args.end, "%y%m%d%H").replace(
531
+ tzinfo=datetime.timezone.utc
532
+ )
533
+ else:
534
+ sys.stderr.write(
535
+ "If begin is specified," + " end has to be specified too.\n"
536
+ )
537
+ sys.exit(1)
538
+ editor.rasterize_emissions(
539
+ args.rasterize,
540
+ args.cellsize,
541
+ substances=substances,
542
+ sourcetypes=args.sourcetypes,
543
+ point_ids=args.point_ids,
544
+ area_ids=args.area_ids,
545
+ grid_ids=args.grid_ids,
546
+ road_ids=args.road_ids,
547
+ unit=args.unit,
548
+ extent=args.extent,
549
+ srid=args.srid,
550
+ begin=args.begin,
551
+ end=args.end,
552
+ ) # TODO add arguments for codeset, substances, begin/end, timezone!
553
+ sys.stdout.write("Successfully rasterized emissions\n")
554
+ sys.exit(0)
555
+
556
+ elif main_args.command == "export":
557
+ sub_parser = argparse.ArgumentParser(
558
+ description="Export data to xlsx-file",
559
+ usage="cetk export <filename> [options]",
560
+ )
561
+ sub_parser.add_argument("filename", help="Path to xslx-file")
562
+ if not Path(db_path).exists():
563
+ sys.stderr.write(
564
+ "Database " + db_path + " does not exist, first run "
565
+ "'cetk create' or 'cetk migrate'\n"
566
+ )
567
+ sys.exit(1)
568
+
569
+ args = sub_parser.parse_args(sub_args)
570
+ try:
571
+ # Check if the file can be created at the given path
572
+ os.access(args.filename, os.W_OK | os.X_OK)
573
+ except Exception as e:
574
+ sys.stderr.write(f"File {args.filename} cannot be created: \n {e} ")
575
+ sys.exit(1)
576
+ status = editor.export_data(args.filename)
577
+ if status:
578
+ sys.stdout.write(f"Exported data from {db_path} to {args.filename}.\n")
579
+ sys.exit(0)
580
+ else:
581
+ sys.stderr.write("Did not export data, something went wrong.")
582
+ sys.exit(1)
583
+
584
+ elif main_args.command == "settings":
585
+ sub_parser = argparse.ArgumentParser(
586
+ description="Update settings",
587
+ usage="cetk settings [--srid EPSG] [--codeset1 SLUG] [--codeset2 SLUG] [--codeset3 SLUG]", # noqa: E501
588
+ )
589
+ sub_parser.add_argument("--srid", type=int, help="Update srid in settings")
590
+ sub_parser.add_argument("--codeset1", help="Update code-set1")
591
+ sub_parser.add_argument("--codeset2", help="Update code-set2")
592
+ sub_parser.add_argument("--codeset3", help="Update code-set3")
593
+ args = sub_parser.parse_args(sub_args)
594
+ editor.update_settings(
595
+ srid=args.srid,
596
+ codeset1=args.codeset1,
597
+ codeset2=args.codeset2,
598
+ codeset3=args.codeset3,
599
+ )
cetk/tools/manage.py ADDED
@@ -0,0 +1,24 @@
1
+ """Djangos management command for cetk."""
2
+
3
+ import os
4
+ import sys
5
+
6
+
7
+ def main():
8
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cetk.settings")
9
+ try:
10
+ from django.core.management import execute_from_command_line
11
+ except ImportError:
12
+ # The above import may fail for some other reason. Ensure that the
13
+ # issue is really that Django is missing to avoid masking other
14
+ # exceptions on Python 2.
15
+ try:
16
+ import django # noqa
17
+ except ImportError:
18
+ raise ImportError(
19
+ "Couldn't import Django. Are you sure it's installed and "
20
+ "available on your PYTHONPATH environment variable? Did you "
21
+ "forget to activate a virtual environment?"
22
+ )
23
+ raise
24
+ execute_from_command_line(sys.argv)