From 9965900791d064d9460d58cb1d1fd1d27a6cbba2 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 21 Jan 2021 13:18:37 +0000 Subject: [PATCH] added geo module, with region supporting gis data --- README.md | 31 + companies/models.py | 2 +- gadm36_ESP.json | 4079 +++++++++++++++++++++++++++++++++++++++++++ geo/__init__.py | 0 geo/admin.py | 10 + geo/apps.py | 5 + geo/factories.py | 49 + geo/models.py | 77 + geo/serializers.py | 68 + geo/tests.py | 712 ++++++++ geo/views.py | 72 + products/models.py | 7 +- 12 files changed, 5108 insertions(+), 4 deletions(-) create mode 100644 gadm36_ESP.json create mode 100644 geo/__init__.py create mode 100644 geo/admin.py create mode 100644 geo/apps.py create mode 100644 geo/factories.py create mode 100644 geo/models.py create mode 100644 geo/serializers.py create mode 100644 geo/tests.py create mode 100644 geo/views.py diff --git a/README.md b/README.md index cf882e5..d9aa99c 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,34 @@ python migrate ``` - Start server in development mode: `python manage.py runserver` + +## Load Geo data +Import geodata + +```python +import os +from geo.models import Region +from django.contrib.gis.geos import GeometryCollection, GEOSGeometry +import json +from django.contrib.gis.geos import MultiPolygon + +path='gadm36_ESP_1.json' + +feature_collection = json.loads(open(path).read()) +for feature in feature_collection['features']: + geom = GEOSGeometry(str(feature['geometry'])) + if feature['geometry']['type'] == "MultiPolygon": + poly_list = [] + for poly in geom: + poly_list.append(poly) + print(poly_list) + else: + poly_list = geom + + geom_geos = MultiPolygon(poly_list) + + name = feature['properties']['NAME_1'] + region = Region.objects.create(name=name,geo=geom_geos) + region.save() + +``` diff --git a/companies/models.py b/companies/models.py index ffacb8e..eb7ea4a 100644 --- a/companies/models.py +++ b/companies/models.py @@ -34,7 +34,7 @@ class Company(models.Model): shop_rss_feed = models.URLField('RSS tienda online', null=True, blank=True) sale_terms = models.TextField('Condiciones de venta', null=True, blank=True) shipping_cost = models.DecimalField('Gastos de envío', max_digits=10, decimal_places=2, null=True, blank=True) - # tags + # tags = models.ManyToMany(Tag, null=True) sync = models.BooleanField('Sincronizar tienda', default=False, null=True, blank=True) # internal diff --git a/gadm36_ESP.json b/gadm36_ESP.json new file mode 100644 index 0000000..75aef20 --- /dev/null +++ b/gadm36_ESP.json @@ -0,0 +1,4079 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -6.93153, + 38.20832062 + ], + [ + -7.0072031, + 38.02075577 + ], + [ + -7.10830688, + 38.04093933 + ], + [ + -7.24558496, + 37.99180222 + ], + [ + -7.32435179, + 37.81582642 + ], + [ + -7.42259884, + 37.75460052 + ], + [ + -7.5226779, + 37.55307388 + ], + [ + -7.44048977, + 37.39102554 + ], + [ + -7.40732718, + 37.18341446 + ], + [ + -7.137918, + 37.21736145 + ], + [ + -7.03291512, + 37.20541763 + ], + [ + -6.97402716, + 37.29624939 + ], + [ + -6.93014002, + 37.1843071 + ], + [ + -6.51708317, + 36.97458267 + ], + [ + -6.39041615, + 36.80347061 + ], + [ + -6.43901396, + 36.72014618 + ], + [ + -6.39263821, + 36.63263702 + ], + [ + -6.22736216, + 36.58124924 + ], + [ + -6.23902893, + 36.46374893 + ], + [ + -6.14374924, + 36.29680634 + ], + [ + -6.05402708, + 36.20291519 + ], + [ + -5.92152691, + 36.18680573 + ], + [ + -5.79680586, + 36.07736206 + ], + [ + -5.58125019, + 36.01263809 + ], + [ + -5.43180704, + 36.06958389 + ], + [ + -5.33902788, + 36.15467453 + ], + [ + -5.21736097, + 36.37930679 + ], + [ + -5.0837512, + 36.45013809 + ], + [ + -4.85958385, + 36.50763702 + ], + [ + -4.63902807, + 36.50624847 + ], + [ + -4.39263916, + 36.72291565 + ], + [ + -4.24763823, + 36.71069336 + ], + [ + -4.06486082, + 36.74874878 + ], + [ + -3.95541692, + 36.72680664 + ], + [ + -3.60069394, + 36.74375153 + ], + [ + -3.42597103, + 36.6954155 + ], + [ + -3.26097298, + 36.7531929 + ], + [ + -2.89541602, + 36.73652649 + ], + [ + -2.69958305, + 36.6823616 + ], + [ + -2.56513906, + 36.81402588 + ], + [ + -2.36736107, + 36.84152603 + ], + [ + -2.1940279, + 36.72097397 + ], + [ + -2.00208402, + 36.83625031 + ], + [ + -1.84958398, + 37.07097244 + ], + [ + -1.80819499, + 37.2140274 + ], + [ + -1.63009703, + 37.37485123 + ], + [ + -1.73698997, + 37.44267654 + ], + [ + -1.84517395, + 37.45486832 + ], + [ + -2.01183605, + 37.67349625 + ], + [ + -1.99518895, + 37.84142685 + ], + [ + -2.17190695, + 37.88873291 + ], + [ + -2.34152889, + 38.02603912 + ], + [ + -2.55119109, + 38.08413315 + ], + [ + -2.4461751, + 38.18522644 + ], + [ + -2.48330903, + 38.39788818 + ], + [ + -2.5665369, + 38.49049759 + ], + [ + -2.76198196, + 38.53278351 + ], + [ + -2.8896699, + 38.45590973 + ], + [ + -3.06486011, + 38.47827911 + ], + [ + -3.13009191, + 38.438797 + ], + [ + -3.37490296, + 38.47526169 + ], + [ + -3.47248602, + 38.39798355 + ], + [ + -3.80774903, + 38.42184448 + ], + [ + -3.96182799, + 38.3661232 + ], + [ + -4.25544024, + 38.40184402 + ], + [ + -4.43505621, + 38.40225983 + ], + [ + -4.62215281, + 38.52349091 + ], + [ + -4.86005783, + 38.61399841 + ], + [ + -4.86876678, + 38.68095398 + ], + [ + -5.0468359, + 38.7291069 + ], + [ + -5.36887121, + 38.58493423 + ], + [ + -5.41883421, + 38.51255035 + ], + [ + -5.5680151, + 38.43340683 + ], + [ + -5.53566217, + 38.16869736 + ], + [ + -5.69350576, + 38.08375931 + ], + [ + -5.83773422, + 38.17444229 + ], + [ + -5.91279507, + 38.12270355 + ], + [ + -5.95356178, + 37.99536133 + ], + [ + -6.1773262, + 37.94608688 + ], + [ + -6.30324316, + 37.97825623 + ], + [ + -6.41377687, + 38.05857468 + ], + [ + -6.58704805, + 38.0267868 + ], + [ + -6.62232685, + 38.09716797 + ], + [ + -6.75809002, + 38.09272003 + ], + [ + -6.79496717, + 38.17854691 + ], + [ + -6.93153, + 38.20832062 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.1_1", + "NAME_1": "Andaluc\u00eda", + "VARNAME_1": "Andalousie|Andaluc\u00a1a|Andalusien|Andaluzia", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "01", + "HASC_1": "ES.AN" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -1.14233398, + 39.97186661 + ], + [ + -0.837883, + 39.97657394 + ], + [ + -0.66405499, + 40.05146408 + ], + [ + -0.49445999, + 40.22875595 + ], + [ + -0.28518999, + 40.38634109 + ], + [ + -0.29323599, + 40.61106491 + ], + [ + -0.194419, + 40.78184509 + ], + [ + 0.042221, + 40.69102859 + ], + [ + 0.17076699, + 40.73286057 + ], + [ + 0.278265, + 40.82106018 + ], + [ + 0.28112701, + 40.98719025 + ], + [ + 0.200949, + 41.10267639 + ], + [ + 0.30335599, + 41.16440201 + ], + [ + 0.38569799, + 41.27886963 + ], + [ + 0.34143099, + 41.4809227 + ], + [ + 0.446955, + 41.54225922 + ], + [ + 0.34964699, + 41.59948349 + ], + [ + 0.328246, + 41.68109131 + ], + [ + 0.46968901, + 41.76564026 + ], + [ + 0.59281301, + 41.88443375 + ], + [ + 0.56246799, + 41.93270874 + ], + [ + 0.65174001, + 42.02626801 + ], + [ + 0.759206, + 42.34838486 + ], + [ + 0.69686902, + 42.47412109 + ], + [ + 0.76754898, + 42.61126328 + ], + [ + 0.66009301, + 42.69099808 + ], + [ + 0.52592242, + 42.70210648 + ], + [ + 0.29318801, + 42.67619705 + ], + [ + 0.17599271, + 42.73564529 + ], + [ + 0.000748, + 42.68521118 + ], + [ + -0.306375, + 42.84132004 + ], + [ + -0.44491899, + 42.79603195 + ], + [ + -0.57361794, + 42.80739975 + ], + [ + -0.72750002, + 42.91926193 + ], + [ + -0.85680199, + 42.84689331 + ], + [ + -0.84750098, + 42.78592682 + ], + [ + -1.03856301, + 42.6483345 + ], + [ + -1.15761602, + 42.61046982 + ], + [ + -1.341465, + 42.42394257 + ], + [ + -1.39903605, + 42.29130554 + ], + [ + -1.39883304, + 42.12622452 + ], + [ + -1.304775, + 42.04295349 + ], + [ + -1.42172897, + 41.91307831 + ], + [ + -1.59619904, + 41.92709732 + ], + [ + -1.84713197, + 42.00800323 + ], + [ + -1.85648596, + 41.96640396 + ], + [ + -1.78771305, + 41.73413849 + ], + [ + -1.96727395, + 41.54607391 + ], + [ + -1.95362496, + 41.40864563 + ], + [ + -2.10145593, + 41.44579697 + ], + [ + -2.17042089, + 41.31884003 + ], + [ + -2.14617491, + 41.18440628 + ], + [ + -2.05162692, + 41.14686203 + ], + [ + -1.81640697, + 41.09529495 + ], + [ + -1.60784101, + 40.92763519 + ], + [ + -1.54461801, + 40.81620789 + ], + [ + -1.53557396, + 40.68703842 + ], + [ + -1.598683, + 40.56207275 + ], + [ + -1.789397, + 40.3973999 + ], + [ + -1.54041696, + 40.19082642 + ], + [ + -1.44879401, + 40.14536285 + ], + [ + -1.14727104, + 40.11396027 + ], + [ + -1.16512597, + 40.01012039 + ], + [ + -1.14233398, + 39.97186661 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.2_1", + "NAME_1": "Arag\u00f3n", + "VARNAME_1": "Arag\u00e3o|Arag\u00f3|Arag\u00f3n|Aragona|Aragonien", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "15", + "HASC_1": "ES.AR" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -4.84093618, + 43.18074036 + ], + [ + -4.85133791, + 43.1259079 + ], + [ + -4.71962214, + 43.01721573 + ], + [ + -4.55836916, + 43.01948166 + ], + [ + -4.4630208, + 43.06002808 + ], + [ + -4.34724808, + 42.97212219 + ], + [ + -4.23841476, + 42.95434189 + ], + [ + -4.22562218, + 42.8588562 + ], + [ + -4.08126211, + 42.76144028 + ], + [ + -3.8215971, + 42.80026627 + ], + [ + -3.82527995, + 42.86861038 + ], + [ + -3.99081397, + 42.93080902 + ], + [ + -3.96074295, + 42.99242401 + ], + [ + -3.71918702, + 43.10832596 + ], + [ + -3.65096092, + 43.18101501 + ], + [ + -3.41759801, + 43.13340759 + ], + [ + -3.43032408, + 43.24207306 + ], + [ + -3.1531949, + 43.3532486 + ], + [ + -3.22263789, + 43.39652634 + ], + [ + -3.44430494, + 43.42291641 + ], + [ + -3.58958411, + 43.51430511 + ], + [ + -3.77124906, + 43.44874954 + ], + [ + -3.81236005, + 43.49458313 + ], + [ + -4.17263889, + 43.40402603 + ], + [ + -4.51221418, + 43.39319611 + ], + [ + -4.63480186, + 43.26873016 + ], + [ + -4.84093618, + 43.18074036 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.3_1", + "NAME_1": "Cantabria", + "VARNAME_1": "Cant\u00e0bria|Cant\u00e1bria|Cantabrie|Kantabrien", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "06", + "HASC_1": "ES.CB" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.33586597, + 40.11582184 + ], + [ + -5.40096807, + 39.88567352 + ], + [ + -5.28341198, + 39.8572197 + ], + [ + -5.25411177, + 39.75255203 + ], + [ + -5.13768387, + 39.71500778 + ], + [ + -5.20195818, + 39.59203339 + ], + [ + -4.90301514, + 39.37519455 + ], + [ + -4.66939306, + 39.4245491 + ], + [ + -4.76090384, + 39.3208046 + ], + [ + -4.7050519, + 39.21376038 + ], + [ + -4.81016302, + 39.19943237 + ], + [ + -4.93355799, + 38.97559357 + ], + [ + -4.91483021, + 38.8874588 + ], + [ + -4.98988199, + 38.73977661 + ], + [ + -5.0468359, + 38.7291069 + ], + [ + -4.86876678, + 38.68095398 + ], + [ + -4.86005783, + 38.61399841 + ], + [ + -4.62215281, + 38.52349091 + ], + [ + -4.43505621, + 38.40225983 + ], + [ + -4.25544024, + 38.40184402 + ], + [ + -3.96182799, + 38.3661232 + ], + [ + -3.80774903, + 38.42184448 + ], + [ + -3.47248602, + 38.39798355 + ], + [ + -3.37490296, + 38.47526169 + ], + [ + -3.13009191, + 38.438797 + ], + [ + -3.06486011, + 38.47827911 + ], + [ + -2.8896699, + 38.45590973 + ], + [ + -2.76198196, + 38.53278351 + ], + [ + -2.5665369, + 38.49049759 + ], + [ + -2.48330903, + 38.39788818 + ], + [ + -2.4461751, + 38.18522644 + ], + [ + -2.55119109, + 38.08413315 + ], + [ + -2.34152889, + 38.02603912 + ], + [ + -2.21342802, + 38.20183945 + ], + [ + -2.06368399, + 38.29762268 + ], + [ + -1.90745401, + 38.29799271 + ], + [ + -1.74343598, + 38.38132095 + ], + [ + -1.58932495, + 38.31123352 + ], + [ + -1.47834694, + 38.37639618 + ], + [ + -1.49838305, + 38.53206635 + ], + [ + -1.44771099, + 38.64936066 + ], + [ + -1.18392897, + 38.75444794 + ], + [ + -1.02605104, + 38.65568924 + ], + [ + -0.91572702, + 38.69599915 + ], + [ + -0.95934498, + 38.94461441 + ], + [ + -1.14658701, + 38.92938614 + ], + [ + -1.26563299, + 39.07549667 + ], + [ + -1.19443095, + 39.180439 + ], + [ + -1.16182303, + 39.30544662 + ], + [ + -1.46254599, + 39.37941742 + ], + [ + -1.51378298, + 39.45798492 + ], + [ + -1.50429499, + 39.56393051 + ], + [ + -1.41786599, + 39.65496063 + ], + [ + -1.31276095, + 39.67050934 + ], + [ + -1.21493804, + 39.80879974 + ], + [ + -1.20391703, + 39.94937134 + ], + [ + -1.14233398, + 39.97186661 + ], + [ + -1.16512597, + 40.01012039 + ], + [ + -1.38307297, + 40.03899002 + ], + [ + -1.44879401, + 40.14536285 + ], + [ + -1.54041696, + 40.19082642 + ], + [ + -1.789397, + 40.3973999 + ], + [ + -1.598683, + 40.56207275 + ], + [ + -1.53557396, + 40.68703842 + ], + [ + -1.54461801, + 40.81620789 + ], + [ + -1.60784101, + 40.92763519 + ], + [ + -1.81640697, + 41.09529495 + ], + [ + -2.05162692, + 41.14686203 + ], + [ + -2.0655551, + 41.09580231 + ], + [ + -2.23207808, + 41.09702301 + ], + [ + -2.32333708, + 41.05651855 + ], + [ + -2.46939492, + 41.07811737 + ], + [ + -2.65476203, + 41.24066162 + ], + [ + -2.95005798, + 41.2926445 + ], + [ + -3.18847895, + 41.30392456 + ], + [ + -3.53969598, + 41.16501236 + ], + [ + -3.39759803, + 41.00736618 + ], + [ + -3.50255299, + 40.7890625 + ], + [ + -3.46159911, + 40.69269943 + ], + [ + -3.20058107, + 40.51469421 + ], + [ + -3.13039207, + 40.40507889 + ], + [ + -3.18346906, + 40.26929092 + ], + [ + -3.079, + 40.22401047 + ], + [ + -3.08856797, + 40.07051086 + ], + [ + -3.27674007, + 40.0474472 + ], + [ + -3.32725501, + 40.07956696 + ], + [ + -3.58957005, + 40.01348114 + ], + [ + -3.60639906, + 40.10900497 + ], + [ + -3.80006909, + 40.17570496 + ], + [ + -3.99234796, + 40.20973206 + ], + [ + -4.35680008, + 40.30965805 + ], + [ + -4.53575802, + 40.1995697 + ], + [ + -4.57889891, + 40.21741104 + ], + [ + -4.76108217, + 40.26094437 + ], + [ + -5.00613308, + 40.10971832 + ], + [ + -5.33586597, + 40.11582184 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.4_1", + "NAME_1": "Castilla-La Mancha", + "VARNAME_1": "Castela-La Mancha|Castela-Mancha|Castella-la Manxa|Castilha-La Mancha", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "08", + "HASC_1": "ES.CM" + } + }, + { + "type": "Feature", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + -2.63755894, + 42.64961243 + ], + [ + -2.56252289, + 42.74126816 + ], + [ + -2.73150492, + 42.79449844 + ], + [ + -2.85415196, + 42.73638535 + ], + [ + -2.76784801, + 42.66600037 + ], + [ + -2.63755894, + 42.64961243 + ] + ] + ], + [ + [ + [ + -3.53969598, + 41.16501236 + ], + [ + -3.18847895, + 41.30392456 + ], + [ + -2.95005798, + 41.2926445 + ], + [ + -2.65476203, + 41.24066162 + ], + [ + -2.46939492, + 41.07811737 + ], + [ + -2.32333708, + 41.05651855 + ], + [ + -2.23207808, + 41.09702301 + ], + [ + -2.0655551, + 41.09580231 + ], + [ + -2.05162692, + 41.14686203 + ], + [ + -2.14617491, + 41.18440628 + ], + [ + -2.17042089, + 41.31884003 + ], + [ + -2.10145593, + 41.44579697 + ], + [ + -1.95362496, + 41.40864563 + ], + [ + -1.96727395, + 41.54607391 + ], + [ + -1.78771305, + 41.73413849 + ], + [ + -1.85648596, + 41.96640396 + ], + [ + -1.961285, + 41.92030716 + ], + [ + -2.1099, + 41.95787048 + ], + [ + -2.16185594, + 42.06673431 + ], + [ + -2.31907892, + 42.14522171 + ], + [ + -2.51461601, + 42.1145401 + ], + [ + -2.5798471, + 41.9962616 + ], + [ + -2.75751901, + 42.03261185 + ], + [ + -2.88375807, + 42.00948334 + ], + [ + -2.93239689, + 42.08089066 + ], + [ + -3.03430605, + 42.08595657 + ], + [ + -3.12879896, + 42.20092773 + ], + [ + -3.05308199, + 42.37340927 + ], + [ + -3.07172108, + 42.52766037 + ], + [ + -3.00469995, + 42.64425278 + ], + [ + -2.85804009, + 42.63819122 + ], + [ + -3.22578812, + 42.8300972 + ], + [ + -3.12648106, + 42.90404129 + ], + [ + -3.01608896, + 42.91053772 + ], + [ + -3.13389301, + 43.09721756 + ], + [ + -3.25337005, + 43.19855881 + ], + [ + -3.41759801, + 43.13340759 + ], + [ + -3.65096092, + 43.18101501 + ], + [ + -3.71918702, + 43.10832596 + ], + [ + -3.96074295, + 42.99242401 + ], + [ + -3.99081397, + 42.93080902 + ], + [ + -3.82527995, + 42.86861038 + ], + [ + -3.8215971, + 42.80026627 + ], + [ + -4.08126211, + 42.76144028 + ], + [ + -4.22562218, + 42.8588562 + ], + [ + -4.23841476, + 42.95434189 + ], + [ + -4.34724808, + 42.97212219 + ], + [ + -4.4630208, + 43.06002808 + ], + [ + -4.55836916, + 43.01948166 + ], + [ + -4.71962214, + 43.01721573 + ], + [ + -4.85133791, + 43.1259079 + ], + [ + -4.84093618, + 43.18074036 + ], + [ + -4.89348888, + 43.23843765 + ], + [ + -5.06372213, + 43.17945099 + ], + [ + -5.10164022, + 43.10181046 + ], + [ + -5.38300085, + 43.08720016 + ], + [ + -5.54030323, + 43.01831436 + ], + [ + -5.68820906, + 43.05683899 + ], + [ + -5.76546097, + 42.96918869 + ], + [ + -5.85021305, + 42.96736908 + ], + [ + -5.97402191, + 43.06490707 + ], + [ + -6.22668314, + 43.0088768 + ], + [ + -6.39691782, + 43.03828812 + ], + [ + -6.44223595, + 42.93929291 + ], + [ + -6.824049, + 42.91502762 + ], + [ + -6.90404606, + 42.75954437 + ], + [ + -7.0450449, + 42.6947403 + ], + [ + -7.05173206, + 42.50862122 + ], + [ + -6.92490911, + 42.51941681 + ], + [ + -6.80845499, + 42.46942902 + ], + [ + -6.7338109, + 42.35909271 + ], + [ + -6.80303288, + 42.23865128 + ], + [ + -6.98967123, + 42.12129974 + ], + [ + -6.98342705, + 41.97294235 + ], + [ + -6.94427919, + 41.94464874 + ], + [ + -6.59943295, + 41.94829178 + ], + [ + -6.51761484, + 41.87507629 + ], + [ + -6.54825306, + 41.68558121 + ], + [ + -6.35464621, + 41.6763916 + ], + [ + -6.18914223, + 41.57481384 + ], + [ + -6.31539392, + 41.3900528 + ], + [ + -6.58051109, + 41.23904419 + ], + [ + -6.64836502, + 41.24753952 + ], + [ + -6.80892086, + 41.03648758 + ], + [ + -6.93162298, + 41.01679993 + ], + [ + -6.80327415, + 40.84621048 + ], + [ + -6.79889822, + 40.65530396 + ], + [ + -6.84969187, + 40.45151138 + ], + [ + -6.78121519, + 40.36379242 + ], + [ + -6.8647871, + 40.27046967 + ], + [ + -6.75544024, + 40.24626923 + ], + [ + -6.58690882, + 40.27072906 + ], + [ + -6.53616905, + 40.34725189 + ], + [ + -6.19857216, + 40.48143768 + ], + [ + -6.0822649, + 40.3632164 + ], + [ + -5.94155407, + 40.28442383 + ], + [ + -5.79740095, + 40.35325241 + ], + [ + -5.60993004, + 40.21548843 + ], + [ + -5.53085613, + 40.19472504 + ], + [ + -5.42971706, + 40.25226212 + ], + [ + -5.33586597, + 40.11582184 + ], + [ + -5.00613308, + 40.10971832 + ], + [ + -4.76108217, + 40.26094437 + ], + [ + -4.57889891, + 40.21741104 + ], + [ + -4.43200016, + 40.39775848 + ], + [ + -4.3240881, + 40.41864014 + ], + [ + -4.3229022, + 40.5511055 + ], + [ + -4.16123199, + 40.62337494 + ], + [ + -4.171875, + 40.67960358 + ], + [ + -4.06996584, + 40.79380035 + ], + [ + -3.97956896, + 40.79984665 + ], + [ + -3.89500809, + 40.96696854 + ], + [ + -3.78176498, + 41.00020218 + ], + [ + -3.61356902, + 41.149086 + ], + [ + -3.53969598, + 41.16501236 + ] + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.5_1", + "NAME_1": "Castilla y Le\u00f3n", + "VARNAME_1": "Castile and Leon|Castela e Le\u00e3o|Castella i Lle\u00f3|Castile-Leon|Castilha-Le\u00e3o|Castilla y Le\u00f3n|Castille et L\u00e9on|Kastilien-Le\u00f3n", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "07", + "HASC_1": "ES.CL" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0.51514, + 40.52296448 + ], + [ + 0.600972, + 40.62208176 + ], + [ + 0.76902902, + 40.64736176 + ], + [ + 0.86958402, + 40.69597244 + ], + [ + 0.70625001, + 40.80958176 + ], + [ + 0.98708302, + 41.04097366 + ], + [ + 1.53208303, + 41.18208313 + ], + [ + 2.11347198, + 41.28958511 + ], + [ + 2.26458406, + 41.45847321 + ], + [ + 2.93374896, + 41.71930695 + ], + [ + 3.2026391, + 41.89125061 + ], + [ + 3.20680594, + 42.07374954 + ], + [ + 3.12486196, + 42.12708282 + ], + [ + 3.14819598, + 42.25902939 + ], + [ + 3.28069496, + 42.25541687 + ], + [ + 3.31680608, + 42.32374954 + ], + [ + 3.16319394, + 42.36041641 + ], + [ + 2.9454689, + 42.48008728 + ], + [ + 2.4984324, + 42.34262466 + ], + [ + 2.25708246, + 42.43848801 + ], + [ + 2.08593321, + 42.36374664 + ], + [ + 1.96436501, + 42.38256073 + ], + [ + 1.91566896, + 42.44618607 + ], + [ + 1.726282, + 42.50491333 + ], + [ + 1.51577103, + 42.42884445 + ], + [ + 1.35737813, + 42.71942139 + ], + [ + 1.16501808, + 42.70952606 + ], + [ + 1.07324803, + 42.78236389 + ], + [ + 0.70818394, + 42.86132813 + ], + [ + 0.66009301, + 42.69099808 + ], + [ + 0.76754898, + 42.61126328 + ], + [ + 0.69686902, + 42.47412109 + ], + [ + 0.759206, + 42.34838486 + ], + [ + 0.65174001, + 42.02626801 + ], + [ + 0.56246799, + 41.93270874 + ], + [ + 0.59281301, + 41.88443375 + ], + [ + 0.46968901, + 41.76564026 + ], + [ + 0.328246, + 41.68109131 + ], + [ + 0.34964699, + 41.59948349 + ], + [ + 0.446955, + 41.54225922 + ], + [ + 0.34143099, + 41.4809227 + ], + [ + 0.38569799, + 41.27886963 + ], + [ + 0.30335599, + 41.16440201 + ], + [ + 0.200949, + 41.10267639 + ], + [ + 0.28112701, + 40.98719025 + ], + [ + 0.278265, + 40.82106018 + ], + [ + 0.17076699, + 40.73286057 + ], + [ + 0.278873, + 40.63008118 + ], + [ + 0.38919899, + 40.60664749 + ], + [ + 0.51514, + 40.52296448 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.6_1", + "NAME_1": "Catalu\u00f1a", + "VARNAME_1": "Catalogna|Catalogne|Catalonia|Catalunha|Catalunya|Katalonien", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "09", + "HASC_1": "ES.CT" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -5.2837491, + 35.90319443 + ], + [ + -5.30958319, + 35.89041519 + ], + [ + -5.38486099, + 35.91152954 + ], + [ + -5.37744284, + 35.88358307 + ], + [ + -5.34124994, + 35.87459946 + ], + [ + -5.2837491, + 35.90319443 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.7_1", + "NAME_1": "Ceuta y Melilla", + "VARNAME_1": "", + "NL_NAME_1": "", + "TYPE_1": "Ciudades Aut\u00f3nomas", + "ENGTYPE_1": "Autonomous City", + "CC_1": "19", + "HASC_1": "ES.ML" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -4.57889891, + 40.21741104 + ], + [ + -4.53575802, + 40.1995697 + ], + [ + -4.35680008, + 40.30965805 + ], + [ + -3.99234796, + 40.20973206 + ], + [ + -3.80006909, + 40.17570496 + ], + [ + -3.60639906, + 40.10900497 + ], + [ + -3.58957005, + 40.01348114 + ], + [ + -3.32725501, + 40.07956696 + ], + [ + -3.27674007, + 40.0474472 + ], + [ + -3.08856797, + 40.07051086 + ], + [ + -3.079, + 40.22401047 + ], + [ + -3.18346906, + 40.26929092 + ], + [ + -3.13039207, + 40.40507889 + ], + [ + -3.20058107, + 40.51469421 + ], + [ + -3.46159911, + 40.69269943 + ], + [ + -3.50255299, + 40.7890625 + ], + [ + -3.39759803, + 41.00736618 + ], + [ + -3.53969598, + 41.16501236 + ], + [ + -3.61356902, + 41.149086 + ], + [ + -3.78176498, + 41.00020218 + ], + [ + -3.89500809, + 40.96696854 + ], + [ + -3.97956896, + 40.79984665 + ], + [ + -4.06996584, + 40.79380035 + ], + [ + -4.171875, + 40.67960358 + ], + [ + -4.16123199, + 40.62337494 + ], + [ + -4.3229022, + 40.5511055 + ], + [ + -4.3240881, + 40.41864014 + ], + [ + -4.43200016, + 40.39775848 + ], + [ + -4.57889891, + 40.21741104 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.8_1", + "NAME_1": "Comunidad de Madrid", + "VARNAME_1": "Madrid|Communaut\u00e9 de Madrid| Community of Madrid|Comunidad de Madrid |Comunidade de Madrid|Comunitat de Madrid", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "", + "HASC_1": "ES.MD" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -1.84713197, + 42.00800323 + ], + [ + -1.59619904, + 41.92709732 + ], + [ + -1.42172897, + 41.91307831 + ], + [ + -1.304775, + 42.04295349 + ], + [ + -1.39883304, + 42.12622452 + ], + [ + -1.39903605, + 42.29130554 + ], + [ + -1.341465, + 42.42394257 + ], + [ + -1.15761602, + 42.61046982 + ], + [ + -1.03856301, + 42.6483345 + ], + [ + -0.84750098, + 42.78592682 + ], + [ + -0.85680199, + 42.84689331 + ], + [ + -0.72750002, + 42.91926193 + ], + [ + -0.97499102, + 42.9632988 + ], + [ + -1.11326838, + 43.02121735 + ], + [ + -1.44139302, + 43.04619598 + ], + [ + -1.38418114, + 43.25322342 + ], + [ + -1.50585639, + 43.29327774 + ], + [ + -1.72886503, + 43.29613113 + ], + [ + -1.92201304, + 43.18671417 + ], + [ + -2.02278996, + 43.06518936 + ], + [ + -2.03830409, + 42.98098755 + ], + [ + -2.23778701, + 42.92144775 + ], + [ + -2.23577189, + 42.83448029 + ], + [ + -2.41574907, + 42.66267395 + ], + [ + -2.42065096, + 42.48929596 + ], + [ + -2.07892203, + 42.36875916 + ], + [ + -1.99547195, + 42.36360931 + ], + [ + -1.79993796, + 42.22280121 + ], + [ + -1.90574896, + 42.06706238 + ], + [ + -1.84713197, + 42.00800323 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.9_1", + "NAME_1": "Comunidad Foral de Navarra", + "VARNAME_1": "Communaut\u00e9 forale de Navarre|Comunidade Foral de Navarra|Comunitat Foral|Navarra", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "15", + "HASC_1": "ES.NA" + } + }, + { + "type": "Feature", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + -1.44879401, + 40.14536285 + ], + [ + -1.38307297, + 40.03899002 + ], + [ + -1.16512597, + 40.01012039 + ], + [ + -1.14727104, + 40.11396027 + ], + [ + -1.44879401, + 40.14536285 + ] + ] + ], + [ + [ + [ + -0.76236099, + 37.84698105 + ], + [ + -0.66013998, + 37.98347092 + ], + [ + -0.63847202, + 38.13680649 + ], + [ + -0.51013899, + 38.20152664 + ], + [ + -0.51208299, + 38.32347107 + ], + [ + -0.37458199, + 38.44291687 + ], + [ + -0.14764, + 38.5368042 + ], + [ + 0.14624999, + 38.68763733 + ], + [ + 0.22041599, + 38.76402664 + ], + [ + 0.112916, + 38.84736252 + ], + [ + -0.014862, + 38.87125015 + ], + [ + -0.135139, + 38.96902847 + ], + [ + -0.23986, + 39.14708328 + ], + [ + -0.223195, + 39.18930435 + ], + [ + -0.334306, + 39.42597198 + ], + [ + -0.31625, + 39.52347183 + ], + [ + -0.213751, + 39.64791489 + ], + [ + -0.087361, + 39.85597229 + ], + [ + -0.005973, + 39.91458511 + ], + [ + 0.049305, + 40.03513718 + ], + [ + 0.13486101, + 40.07347107 + ], + [ + 0.40319499, + 40.36152649 + ], + [ + 0.51514, + 40.52296448 + ], + [ + 0.38919899, + 40.60664749 + ], + [ + 0.278873, + 40.63008118 + ], + [ + 0.17076699, + 40.73286057 + ], + [ + 0.042221, + 40.69102859 + ], + [ + -0.194419, + 40.78184509 + ], + [ + -0.29323599, + 40.61106491 + ], + [ + -0.28518999, + 40.38634109 + ], + [ + -0.49445999, + 40.22875595 + ], + [ + -0.66405499, + 40.05146408 + ], + [ + -0.837883, + 39.97657394 + ], + [ + -1.14233398, + 39.97186661 + ], + [ + -1.20391703, + 39.94937134 + ], + [ + -1.21493804, + 39.80879974 + ], + [ + -1.31276095, + 39.67050934 + ], + [ + -1.41786599, + 39.65496063 + ], + [ + -1.50429499, + 39.56393051 + ], + [ + -1.51378298, + 39.45798492 + ], + [ + -1.46254599, + 39.37941742 + ], + [ + -1.16182303, + 39.30544662 + ], + [ + -1.19443095, + 39.180439 + ], + [ + -1.26563299, + 39.07549667 + ], + [ + -1.14658701, + 38.92938614 + ], + [ + -0.95934498, + 38.94461441 + ], + [ + -0.91572702, + 38.69599915 + ], + [ + -1.02605104, + 38.65568924 + ], + [ + -1.01385498, + 38.49708557 + ], + [ + -1.08173001, + 38.44186401 + ], + [ + -1.08440804, + 38.34651566 + ], + [ + -0.96774602, + 38.25588226 + ], + [ + -1.03658497, + 38.1374321 + ], + [ + -0.921251, + 37.9445343 + ], + [ + -0.76236099, + 37.84698105 + ] + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.10_1", + "NAME_1": "Comunidad Valenciana", + "VARNAME_1": "Valencia|Communaut\u00e9 de Valence|Comunidade Valenciana|Comunidad Valenciana|Comunitat Valenciana", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "10", + "HASC_1": "ES.VC" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -6.8647871, + 40.27046967 + ], + [ + -7.01269197, + 40.22544861 + ], + [ + -7.01514196, + 40.13559723 + ], + [ + -6.88093376, + 40.04166794 + ], + [ + -6.90359688, + 39.87069702 + ], + [ + -6.9872098, + 39.81002045 + ], + [ + -7.01552296, + 39.6704216 + ], + [ + -7.33116579, + 39.64099121 + ], + [ + -7.53376484, + 39.66716385 + ], + [ + -7.49941301, + 39.58960342 + ], + [ + -7.29426813, + 39.45682144 + ], + [ + -7.31034899, + 39.34095764 + ], + [ + -7.23148108, + 39.27825546 + ], + [ + -7.22161722, + 39.19329453 + ], + [ + -7.02820206, + 39.11565018 + ], + [ + -6.95907211, + 39.01730347 + ], + [ + -7.0330348, + 38.87882233 + ], + [ + -7.26004601, + 38.72294235 + ], + [ + -7.26391792, + 38.58786774 + ], + [ + -7.31791115, + 38.44009018 + ], + [ + -7.14295912, + 38.26171875 + ], + [ + -7.097785, + 38.17925644 + ], + [ + -6.93153, + 38.20832062 + ], + [ + -6.79496717, + 38.17854691 + ], + [ + -6.75809002, + 38.09272003 + ], + [ + -6.62232685, + 38.09716797 + ], + [ + -6.58704805, + 38.0267868 + ], + [ + -6.41377687, + 38.05857468 + ], + [ + -6.30324316, + 37.97825623 + ], + [ + -6.1773262, + 37.94608688 + ], + [ + -5.95356178, + 37.99536133 + ], + [ + -5.91279507, + 38.12270355 + ], + [ + -5.83773422, + 38.17444229 + ], + [ + -5.69350576, + 38.08375931 + ], + [ + -5.53566217, + 38.16869736 + ], + [ + -5.5680151, + 38.43340683 + ], + [ + -5.41883421, + 38.51255035 + ], + [ + -5.36887121, + 38.58493423 + ], + [ + -5.0468359, + 38.7291069 + ], + [ + -4.98988199, + 38.73977661 + ], + [ + -4.91483021, + 38.8874588 + ], + [ + -4.93355799, + 38.97559357 + ], + [ + -4.81016302, + 39.19943237 + ], + [ + -4.7050519, + 39.21376038 + ], + [ + -4.76090384, + 39.3208046 + ], + [ + -4.66939306, + 39.4245491 + ], + [ + -4.90301514, + 39.37519455 + ], + [ + -5.20195818, + 39.59203339 + ], + [ + -5.13768387, + 39.71500778 + ], + [ + -5.25411177, + 39.75255203 + ], + [ + -5.28341198, + 39.8572197 + ], + [ + -5.40096807, + 39.88567352 + ], + [ + -5.33586597, + 40.11582184 + ], + [ + -5.42971706, + 40.25226212 + ], + [ + -5.53085613, + 40.19472504 + ], + [ + -5.60993004, + 40.21548843 + ], + [ + -5.79740095, + 40.35325241 + ], + [ + -5.94155407, + 40.28442383 + ], + [ + -6.0822649, + 40.3632164 + ], + [ + -6.19857216, + 40.48143768 + ], + [ + -6.53616905, + 40.34725189 + ], + [ + -6.58690882, + 40.27072906 + ], + [ + -6.75544024, + 40.24626923 + ], + [ + -6.8647871, + 40.27046967 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.11_1", + "NAME_1": "Extremadura", + "VARNAME_1": "Estremadura|Estr\u00e9madure", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "11", + "HASC_1": "ES.EX" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -7.05263901, + 43.47458267 + ], + [ + -7.04152822, + 43.55708313 + ], + [ + -7.29541683, + 43.59402847 + ], + [ + -7.35597181, + 43.67013931 + ], + [ + -7.67652702, + 43.76958466 + ], + [ + -7.80763912, + 43.7168045 + ], + [ + -7.86013889, + 43.76486206 + ], + [ + -8.00597191, + 43.70430374 + ], + [ + -8.3231926, + 43.5256958 + ], + [ + -8.30986023, + 43.45430374 + ], + [ + -8.50874901, + 43.32624817 + ], + [ + -8.71791744, + 43.28958511 + ], + [ + -8.91402721, + 43.31986237 + ], + [ + -9.06208229, + 43.18375015 + ], + [ + -9.21513844, + 43.15180588 + ], + [ + -9.29819393, + 42.92152786 + ], + [ + -9.14902687, + 42.88236237 + ], + [ + -9.11013794, + 42.81402588 + ], + [ + -8.98097229, + 42.7784729 + ], + [ + -9.07985973, + 42.59791565 + ], + [ + -8.9831953, + 42.54347229 + ], + [ + -8.85097313, + 42.66597366 + ], + [ + -8.76597214, + 42.60235977 + ], + [ + -8.88152695, + 42.45486069 + ], + [ + -8.84013844, + 42.38458252 + ], + [ + -8.86458397, + 42.27819443 + ], + [ + -8.76291561, + 42.23152924 + ], + [ + -8.89597225, + 42.09958267 + ], + [ + -8.88208294, + 41.88097382 + ], + [ + -8.74770546, + 41.94314957 + ], + [ + -8.63675594, + 42.0474968 + ], + [ + -8.33186436, + 42.08383942 + ], + [ + -8.19536018, + 42.1482048 + ], + [ + -8.08600807, + 42.01649475 + ], + [ + -8.21687508, + 41.91315842 + ], + [ + -8.093894, + 41.8080368 + ], + [ + -7.92127991, + 41.88181686 + ], + [ + -7.70411682, + 41.90733719 + ], + [ + -7.57388783, + 41.82971573 + ], + [ + -7.45247412, + 41.86507797 + ], + [ + -7.31528997, + 41.84254074 + ], + [ + -7.19671202, + 41.87997437 + ], + [ + -7.18663216, + 41.96942902 + ], + [ + -6.98342705, + 41.97294235 + ], + [ + -6.98967123, + 42.12129974 + ], + [ + -6.80303288, + 42.23865128 + ], + [ + -6.7338109, + 42.35909271 + ], + [ + -6.80845499, + 42.46942902 + ], + [ + -6.92490911, + 42.51941681 + ], + [ + -7.05173206, + 42.50862122 + ], + [ + -7.0450449, + 42.6947403 + ], + [ + -6.90404606, + 42.75954437 + ], + [ + -6.824049, + 42.91502762 + ], + [ + -6.96430683, + 43.02653122 + ], + [ + -6.82746696, + 43.12314987 + ], + [ + -7.06514215, + 43.24941254 + ], + [ + -7.17848396, + 43.38797379 + ], + [ + -7.05263901, + 43.47458267 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.12_1", + "NAME_1": "Galicia", + "VARNAME_1": "Galice|Gal\u00a1cia|Galicien|Galiza|Galizia", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "12", + "HASC_1": "ES.GA" + } + }, + { + "type": "Feature", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + 1.22680402, + 38.8745842 + ], + [ + 1.40236199, + 38.84875107 + ], + [ + 1.60819495, + 39.0359726 + ], + [ + 1.54236197, + 39.11624908 + ], + [ + 1.35874999, + 39.07402802 + ], + [ + 1.28986096, + 39.02708435 + ], + [ + 1.22680402, + 38.8745842 + ] + ] + ], + [ + [ + [ + 4.30263901, + 39.84222412 + ], + [ + 4.2562499, + 39.96541595 + ], + [ + 4.03597116, + 40.06458282 + ], + [ + 3.82736206, + 40.05374908 + ], + [ + 3.81986094, + 39.9268074 + ], + [ + 4.02069378, + 39.92180634 + ], + [ + 4.23680592, + 39.81402969 + ], + [ + 4.30263901, + 39.84222412 + ] + ] + ], + [ + [ + [ + 3.03069496, + 39.28866577 + ], + [ + 3.23236203, + 39.36125183 + ], + [ + 3.302917, + 39.50402832 + ], + [ + 3.4534719, + 39.66930389 + ], + [ + 3.37958193, + 39.76569366 + ], + [ + 3.2431941, + 39.72958374 + ], + [ + 3.15180612, + 39.76819611 + ], + [ + 3.05513811, + 39.92152786 + ], + [ + 2.94652796, + 39.91791534 + ], + [ + 2.73819494, + 39.83124924 + ], + [ + 2.54986095, + 39.69930649 + ], + [ + 2.36958408, + 39.61458206 + ], + [ + 2.36458302, + 39.53097153 + ], + [ + 2.54097199, + 39.52430725 + ], + [ + 2.66430497, + 39.5620842 + ], + [ + 2.78986096, + 39.36208344 + ], + [ + 2.95736098, + 39.36347198 + ], + [ + 3.03069496, + 39.28866577 + ] + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.13_1", + "NAME_1": "Islas Baleares", + "VARNAME_1": "Balearic Islands|Balearen|Balearene|Baleares|Islas Baleares|Baleari|\u00celes Bal\u00e9ares|Ilhas Baleares|Illes Balears", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "04", + "HASC_1": "ES.PM" + } + }, + { + "type": "Feature", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + -18.04791641, + 27.69041634 + ], + [ + -17.95736122, + 27.71902847 + ], + [ + -17.88263893, + 27.80291748 + ], + [ + -17.95958328, + 27.84180641 + ], + [ + -18.04791641, + 27.69041634 + ] + ] + ], + [ + [ + [ + -15.44513893, + 27.79834175 + ], + [ + -15.37374973, + 27.99819374 + ], + [ + -15.41069412, + 28.10347176 + ], + [ + -15.62958336, + 28.16847229 + ], + [ + -15.83347225, + 27.97291756 + ], + [ + -15.78736019, + 27.8334713 + ], + [ + -15.59958458, + 27.73430634 + ], + [ + -15.44513893, + 27.79834175 + ] + ] + ], + [ + [ + [ + -13.69728374, + 28.91791534 + ], + [ + -13.48569489, + 28.99541664 + ], + [ + -13.42291737, + 29.20902824 + ], + [ + -13.49763775, + 29.21791649 + ], + [ + -13.5484724, + 29.11791611 + ], + [ + -13.65847206, + 29.12513924 + ], + [ + -13.81680584, + 29.03347206 + ], + [ + -13.87708473, + 28.85874939 + ], + [ + -13.77791691, + 28.84375191 + ], + [ + -13.69728374, + 28.91791534 + ] + ] + ], + [ + [ + [ + -17.84736252, + 28.4556942 + ], + [ + -17.76236153, + 28.56652832 + ], + [ + -17.72569466, + 28.73347282 + ], + [ + -17.78819466, + 28.84375191 + ], + [ + -17.91069412, + 28.85791588 + ], + [ + -18.00875282, + 28.78041649 + ], + [ + -17.84736252, + 28.4556942 + ] + ] + ], + [ + [ + [ + -16.85239029, + 28.2743187 + ], + [ + -16.83763885, + 28.20763969 + ], + [ + -16.70180321, + 28.00402832 + ], + [ + -16.52124977, + 28.05319405 + ], + [ + -16.43486023, + 28.14041519 + ], + [ + -16.36208344, + 28.30486107 + ], + [ + -16.35986137, + 28.37986183 + ], + [ + -16.11958313, + 28.55652618 + ], + [ + -16.31513977, + 28.57180786 + ], + [ + -16.38041687, + 28.54791641 + ], + [ + -16.5106945, + 28.42041588 + ], + [ + -16.92319489, + 28.35541534 + ], + [ + -16.85239029, + 28.2743187 + ] + ] + ], + [ + [ + [ + -17.20819473, + 28.02402878 + ], + [ + -17.11347008, + 28.08708572 + ], + [ + -17.20152855, + 28.20069504 + ], + [ + -17.30847168, + 28.20263863 + ], + [ + -17.34902954, + 28.09763908 + ], + [ + -17.20819473, + 28.02402878 + ] + ] + ], + [ + [ + [ + -14.49402809, + 28.08569336 + ], + [ + -14.32680607, + 28.0470829 + ], + [ + -14.2256937, + 28.16013908 + ], + [ + -13.93541718, + 28.23291588 + ], + [ + -13.84847164, + 28.40847206 + ], + [ + -13.82986069, + 28.69236183 + ], + [ + -13.88680458, + 28.75763893 + ], + [ + -13.97764015, + 28.73625183 + ], + [ + -14.09875107, + 28.47874832 + ], + [ + -14.20958328, + 28.31319427 + ], + [ + -14.21541786, + 28.22597313 + ], + [ + -14.31763935, + 28.14236069 + ], + [ + -14.49402809, + 28.08569336 + ] + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.14_1", + "NAME_1": "Islas Canarias", + "VARNAME_1": "Canarias|Canary Islands|Can\u00e1rias|Ilhas Can\u00e1rias|Canarie|\u00celes Canaries|Illes Can\u00e0ries|Kanari\u00f8yene|Kanarische Inseln", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "05", + "HASC_1": "ES.CN" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -2.85804009, + 42.63819122 + ], + [ + -3.00469995, + 42.64425278 + ], + [ + -3.07172108, + 42.52766037 + ], + [ + -3.05308199, + 42.37340927 + ], + [ + -3.12879896, + 42.20092773 + ], + [ + -3.03430605, + 42.08595657 + ], + [ + -2.93239689, + 42.08089066 + ], + [ + -2.88375807, + 42.00948334 + ], + [ + -2.75751901, + 42.03261185 + ], + [ + -2.5798471, + 41.9962616 + ], + [ + -2.51461601, + 42.1145401 + ], + [ + -2.31907892, + 42.14522171 + ], + [ + -2.16185594, + 42.06673431 + ], + [ + -2.1099, + 41.95787048 + ], + [ + -1.961285, + 41.92030716 + ], + [ + -1.85648596, + 41.96640396 + ], + [ + -1.84713197, + 42.00800323 + ], + [ + -1.90574896, + 42.06706238 + ], + [ + -1.79993796, + 42.22280121 + ], + [ + -1.99547195, + 42.36360931 + ], + [ + -2.07892203, + 42.36875916 + ], + [ + -2.42065096, + 42.48929596 + ], + [ + -2.65648699, + 42.50404358 + ], + [ + -2.68048906, + 42.59450912 + ], + [ + -2.85804009, + 42.63819122 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.15_1", + "NAME_1": "La Rioja", + "VARNAME_1": "Rioja", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "17", + "HASC_1": "ES.LO" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -3.41759801, + 43.13340759 + ], + [ + -3.25337005, + 43.19855881 + ], + [ + -3.13389301, + 43.09721756 + ], + [ + -3.01608896, + 42.91053772 + ], + [ + -3.12648106, + 42.90404129 + ], + [ + -3.22578812, + 42.8300972 + ], + [ + -2.85804009, + 42.63819122 + ], + [ + -2.68048906, + 42.59450912 + ], + [ + -2.65648699, + 42.50404358 + ], + [ + -2.42065096, + 42.48929596 + ], + [ + -2.41574907, + 42.66267395 + ], + [ + -2.23577189, + 42.83448029 + ], + [ + -2.23778701, + 42.92144775 + ], + [ + -2.03830409, + 42.98098755 + ], + [ + -2.02278996, + 43.06518936 + ], + [ + -1.92201304, + 43.18671417 + ], + [ + -1.72886503, + 43.29613113 + ], + [ + -1.80236101, + 43.39014053 + ], + [ + -2.12708306, + 43.29097366 + ], + [ + -2.35597205, + 43.30291748 + ], + [ + -2.73847198, + 43.4295845 + ], + [ + -2.94708395, + 43.43597031 + ], + [ + -3.03402805, + 43.36763763 + ], + [ + -3.1531949, + 43.3532486 + ], + [ + -3.43032408, + 43.24207306 + ], + [ + -3.41759801, + 43.13340759 + ] + ], + [ + [ + -2.63755894, + 42.64961243 + ], + [ + -2.76784801, + 42.66600037 + ], + [ + -2.85415196, + 42.73638535 + ], + [ + -2.73150492, + 42.79449844 + ], + [ + -2.56252289, + 42.74126816 + ], + [ + -2.63755894, + 42.64961243 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.16_1", + "NAME_1": "Pa\u00eds Vasco", + "VARNAME_1": "Basque Country|Baskenland|Basque Autonomous Community|Basque Provinces|CAV|Comunidad Autonoma Vasca", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "16", + "HASC_1": "ES.PV" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -4.84093618, + 43.18074036 + ], + [ + -4.63480186, + 43.26873016 + ], + [ + -4.51221418, + 43.39319611 + ], + [ + -4.6537509, + 43.40097046 + ], + [ + -4.93847179, + 43.45930481 + ], + [ + -5.20763779, + 43.47402954 + ], + [ + -5.4151392, + 43.5545845 + ], + [ + -5.69458389, + 43.54513931 + ], + [ + -5.8340292, + 43.64541626 + ], + [ + -5.94513893, + 43.58430481 + ], + [ + -6.11736202, + 43.55486298 + ], + [ + -6.24263906, + 43.5890274 + ], + [ + -6.35708284, + 43.55097198 + ], + [ + -7.02291679, + 43.55680466 + ], + [ + -7.05263901, + 43.47458267 + ], + [ + -7.17848396, + 43.38797379 + ], + [ + -7.06514215, + 43.24941254 + ], + [ + -6.82746696, + 43.12314987 + ], + [ + -6.96430683, + 43.02653122 + ], + [ + -6.824049, + 42.91502762 + ], + [ + -6.44223595, + 42.93929291 + ], + [ + -6.39691782, + 43.03828812 + ], + [ + -6.22668314, + 43.0088768 + ], + [ + -5.97402191, + 43.06490707 + ], + [ + -5.85021305, + 42.96736908 + ], + [ + -5.76546097, + 42.96918869 + ], + [ + -5.68820906, + 43.05683899 + ], + [ + -5.54030323, + 43.01831436 + ], + [ + -5.38300085, + 43.08720016 + ], + [ + -5.10164022, + 43.10181046 + ], + [ + -5.06372213, + 43.17945099 + ], + [ + -4.89348888, + 43.23843765 + ], + [ + -4.84093618, + 43.18074036 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.17_1", + "NAME_1": "Principado de Asturias", + "VARNAME_1": "Ast\u00farias|Asturie|Asturien|Asturies|Ast\u00faries|Asturias", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "03", + "HASC_1": "ES.AS" + } + }, + { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -2.34152889, + 38.02603912 + ], + [ + -2.17190695, + 37.88873291 + ], + [ + -1.99518895, + 37.84142685 + ], + [ + -2.01183605, + 37.67349625 + ], + [ + -1.84517395, + 37.45486832 + ], + [ + -1.73698997, + 37.44267654 + ], + [ + -1.63009703, + 37.37485123 + ], + [ + -1.32097304, + 37.56319427 + ], + [ + -1.12625003, + 37.55374908 + ], + [ + -1.00797701, + 37.58541489 + ], + [ + -0.91509998, + 37.55908203 + ], + [ + -0.71763802, + 37.60680389 + ], + [ + -0.85958302, + 37.72291565 + ], + [ + -0.76236099, + 37.84698105 + ], + [ + -0.921251, + 37.9445343 + ], + [ + -1.03658497, + 38.1374321 + ], + [ + -0.96774602, + 38.25588226 + ], + [ + -1.08440804, + 38.34651566 + ], + [ + -1.08173001, + 38.44186401 + ], + [ + -1.01385498, + 38.49708557 + ], + [ + -1.02605104, + 38.65568924 + ], + [ + -1.18392897, + 38.75444794 + ], + [ + -1.44771099, + 38.64936066 + ], + [ + -1.49838305, + 38.53206635 + ], + [ + -1.47834694, + 38.37639618 + ], + [ + -1.58932495, + 38.31123352 + ], + [ + -1.74343598, + 38.38132095 + ], + [ + -1.90745401, + 38.29799271 + ], + [ + -2.06368399, + 38.29762268 + ], + [ + -2.21342802, + 38.20183945 + ], + [ + -2.34152889, + 38.02603912 + ] + ] + ] + }, + "properties": { + "GID_0": "ESP", + "NAME_0": "Spain", + "GID_1": "ESP.18_1", + "NAME_1": "Regi\u00f3n de Murcia", + "VARNAME_1": "Murcia|Regi\u00e3o de M\u00farcia|Regi\u00f3 de M\u00farcia|R\u00e9gion de Murcie|Region of Murcia", + "NL_NAME_1": "", + "TYPE_1": "Comunidad Aut\u00f3noma", + "ENGTYPE_1": "Autonomous Community", + "CC_1": "14", + "HASC_1": "ES.MU" + } + } + ] +} \ No newline at end of file diff --git a/geo/__init__.py b/geo/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/geo/admin.py b/geo/admin.py new file mode 100644 index 0000000..867fa08 --- /dev/null +++ b/geo/admin.py @@ -0,0 +1,10 @@ +from django.contrib import admin + +from . import models + +# Register your models here. + +admin.site.register(models.Country) +admin.site.register(models.Region) +admin.site.register(models.City) + diff --git a/geo/apps.py b/geo/apps.py new file mode 100644 index 0000000..4245e2a --- /dev/null +++ b/geo/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class GeoConfig(AppConfig): + name = 'geo' diff --git a/geo/factories.py b/geo/factories.py new file mode 100644 index 0000000..2e0309f --- /dev/null +++ b/geo/factories.py @@ -0,0 +1,49 @@ +import random + +from factory import LazyAttribute, SubFactory +from factory.fuzzy import FuzzyText, FuzzyInteger +from factory.django import DjangoModelFactory + + +class CountryFactory(DjangoModelFactory): + """ + Country model factory + """ + name = FuzzyText(length=12, suffix="_test_name") + + class Meta: + model = 'geo.Country' + + +class RegionFactory(DjangoModelFactory): + """ + Region model factory + """ + name = FuzzyText(length=12, suffix="_test_name") + country = SubFactory('geo.factories.CountryFactory') + + class Meta: + model = 'geo.Region' + + +class ProvinceFactory(DjangoModelFactory): + """ + City model factory + """ + name = FuzzyText(length=12, suffix="_test_name") + region = SubFactory('geo.factories.RegionFactory') + + class Meta: + model = 'geo.Province' + + +class CityFactory(DjangoModelFactory): + """ + City model factory + """ + name = FuzzyText(length=12, suffix="_test_name") + province = SubFactory('geo.factories.ProvinceFactory') + + class Meta: + model = 'geo.City' + diff --git a/geo/models.py b/geo/models.py new file mode 100644 index 0000000..8895804 --- /dev/null +++ b/geo/models.py @@ -0,0 +1,77 @@ +from django.contrib.gis.db import models + + +class Country(models.Model): + """ + Country model + """ + name = models.CharField(max_length = 100) + + # internal + created = models.DateTimeField('date of creation', auto_now_add=True) + updated = models.DateTimeField('date last update', auto_now=True) + + def __str__(self): + return f'{self.name}' + + class Meta: + verbose_name = "País" + verbose_name_plural = "Paises" + + +class Region(models.Model): + """ + Region model + """ + name = models.CharField(max_length=250) + country = models.ForeignKey(Country,on_delete=models.DO_NOTHING,related_name='regions') + geo = models.MultiPolygonField(null=True) + + # internal + created = models.DateTimeField('date of creation', auto_now_add=True) + updated = models.DateTimeField('date last update', auto_now=True) + + def __str__(self): + return f'{self.name} [{self.country}]' + + class Meta: + verbose_name = "Región" + verbose_name_plural = "Regiones" + + +class Province(models.Model): + """ + Country model + """ + name = models.CharField(max_length = 100) + region = models.ForeignKey(Region, on_delete=models.DO_NOTHING, related_name='province') + + # internal + created = models.DateTimeField('date of creation', auto_now_add=True) + updated = models.DateTimeField('date last update', auto_now=True) + + def __str__(self): + return f'{self.name} [{self.region}]' + + class Meta: + verbose_name = "Provincia" + verbose_name_plural = "Provincias" + + +class City(models.Model): + """ + City model + """ + name = models.CharField(max_length = 250) + province = models.ForeignKey(Province, on_delete=models.DO_NOTHING, related_name='city', null=True) + + # internal + created = models.DateTimeField('date of creation', auto_now_add=True) + updated = models.DateTimeField('date last update', auto_now=True) + + def __str__(self): + return f'{self.name} [{self.province}]' + + class Meta: + verbose_name = "Municipio" + verbose_name_plural = "Municipios" diff --git a/geo/serializers.py b/geo/serializers.py new file mode 100644 index 0000000..1ef38be --- /dev/null +++ b/geo/serializers.py @@ -0,0 +1,68 @@ +from rest_framework import serializers + +from . import models + + +class CountryReadSerializer(serializers.ModelSerializer): + + class Meta: + model = models.Country + fields = '__all__' + + +class CountryWriteSerializer(CustomWriteSerializer): + + class Meta: + model = models.Country + fields = '__all__' + + +class RegionWriteSerializer(CustomWriteSerializer): + country = serializers.IntegerField() + + def validate_country(self, value): + return models.Country.objects.using(self.context['db']).filter(id=value).first() + + class Meta: + model = models.Region + fields = '__all__' + + +class RegionReadSerializer(serializers.ModelSerializer): + + country = CountryReadSerializer() + + class Meta: + model = models.Region + fields = '__all__' + + +class ProvinceReadSerializer(serializers.ModelSerializer): + + region = RegionReadSerializer() + + class Meta: + model = models.Province + fields = '__all__' + + +class CityWriteSerializer(CustomWriteSerializer): + + region = serializers.IntegerField() + + def validate_region(self, value): + return models.Region.objects.using(self.context['db']).filter(id=value).first() + + class Meta: + model = models.City + fields = '__all__' + + +class CityReadSerializer(serializers.ModelSerializer): + + province = ProvinceReadSerializer() + + class Meta: + model = models.City + fields = '__all__' + diff --git a/geo/tests.py b/geo/tests.py new file mode 100644 index 0000000..f6f5990 --- /dev/null +++ b/geo/tests.py @@ -0,0 +1,712 @@ +import random +import datetime + +from django.urls import reverse +from django.test import TestCase +from django.utils import timezone + +from rest_framework.test import APITestCase +from rest_framework import status + +import jwt + +from tenants.factories import TenantUserFactory + +from . import models +from . import factories + + +# Model Tests +class CountryTest(TestCase): + """Country model tests + """ + + def setUp(self): + """Tests setup + """ + self.model = models.Country + + def test_content(self): + """Test content correctly set + """ + # Define instance data + data = { + 'name': 'country name _test_ data' + } + + # Create instance + instance = self.model.objects.create(**data) + + # Assert content correctly set + for field in data.keys(): + self.assertEqual(getattr(instance, field), data[field]) + + +class RegionTest(TestCase): + """Region model tests + """ + + def setUp(self): + """Tests setup + """ + self.model = models.Region + + def test_content(self): + """Test content correctly set + """ + # Define instance data + data = { + 'name': 'region name _test_ data', + 'country': factories.CountryFactory(), + } + + # Create instance + instance = self.model.objects.create(**data) + + # Assert content correctly set + for field in data.keys(): + self.assertEqual(getattr(instance, field), data[field]) + + +class CityTest(TestCase): + """City model tests + """ + + def setUp(self): + """Tests setup + """ + self.model = models.City + + def test_content(self): + """Test content correctly set + """ + # Define instance data + data = { + 'name': 'city name _test_ data', + 'region': factories.RegionFactory(), + } + + # Create instance + instance = self.model.objects.create(**data) + + # Assert content correctly set + for field in data.keys(): + self.assertEqual(getattr(instance, field), data[field]) + + +class JurisdictionTest(TestCase): + """Jurisdiction model tests + """ + + def setUp(self): + """Tests setup + """ + self.model = models.Jurisdiction + + def test_content(self): + """Test content correctly set + """ + # Define instance data + data = { + 'name': 'jurisdiction name test data', + 'region': factories.RegionFactory(), + } + + # Create instance + instance = self.model.objects.create(**data) + + # Assert content correctly set + for field in data.keys(): + self.assertEqual(getattr(instance, field), data[field]) + + +# ViewSet Tests +class CountryViewSetTest(APITestCase): + """Country viewset tests + """ + databases = {'default', 'tenants'} + + def setUp(self): + """Tests setup + """ + self.endpoint = '/api/v1/countries/' + self.factory = factories.CountryFactory + self.model = models.Country + + def test_not_logged_user_cannot_create_country(self): + """Not logged-in user cannot create new country + """ + # Query endpoint + response = self.client.post(self.endpoint, data={}) + + # Assert access is forbidden + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_not_logged_user_cannot_modify_existing_country(self): + """Not logged-in user cannot modify existing country + """ + # Create instance + instance = self.factory() + + # Query endpoint + url = self.endpoint + f'{instance.pk}/' + response = self.client.put(url, {}, format='json') + + # Assert forbidden code + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_not_logged_user_cannot_delete_existing_country(self): + """Not logged-in user cannot delete existing country + """ + # Create instances + instance = self.factory() + + # Query endpoint + url = self.endpoint + f'{instance.pk}/' + response = self.client.delete(url) + + # Assert instance still exists on db + self.assertTrue(self.model.objects.get(id=instance.pk)) + + def test_not_logged_user_cannot_list_country(self): + """Not logged-in user can't read country + """ + # Request list + # url = reverse(self.endpoint+'list') + response = self.client.get(self.endpoint) + + # Assert access is forbidden + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_logged_user_can_list_country(self): + """Regular logged-in user can list country + """ + # Create instances + user = TenantUserFactory() + instances = [self.factory() for n in range(random.randint(1,5))] + + # Authenticate user + user.db = 'default' + self.client.force_authenticate(user=user) + + # Request list + response = self.client.get(self.endpoint) + + # Assert access is allowed + self.assertEqual(response.status_code, status.HTTP_200_OK) + + # Assert all instances are returned + self.assertEqual(len(instances), len(response.data['results'])) + + def test_logged_user_can_create_country(self): + """Regular logged-in user can create new country + """ + # Define request data + data = { + 'name': 'country name _test_ data', + } + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + response = self.client.post(self.endpoint, data=data) + # Assert endpoint returns created status + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + # Assert instance exists on db + self.assertTrue(self.model.objects.get(id=response.data['id'])) + + def test_logged_user_can_modify_existing_country(self): + """Regular logged-in user can modify existing country + """ + # Create instances + instance = self.factory() + + # Define request data + data = { + 'name': 'country name _test_ data', + } + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + url = self.endpoint + f'{instance.pk}/' + response = self.client.put(url, data=data, format='json') + # Assert endpoint returns OK code + self.assertEqual(response.status_code, status.HTTP_200_OK) + + # Assert instance has been modified + for key in data: + self.assertEqual(data[key], response.data[key]) + + def test_logged_user_can_delete_existing_country(self): + """Regular logged-in user can delete existing country + """ + # Create instances + instance = self.factory() + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + url = self.endpoint + f'{instance.pk}/' + response = self.client.delete(url) + # assert 204 no content + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + # Assert instance doesn't exists anymore on db + self.assertFalse(self.model.objects.filter(id=instance.pk).exists()) + + +class RegionViewSetTest(APITestCase): + """Region viewset tests + """ + databases = {'default', 'tenants'} + + def setUp(self): + """Tests setup + """ + self.endpoint = 'api-v1:region-' + self.factory = factories.RegionFactory + self.model = models.Region + + def test_not_logged_user_cannot_create_region(self): + """Not logged-in user cannot create new region + """ + # Query endpoint + url = reverse(self.endpoint+'list') + response = self.client.post(url, data={}) + + # Assert access is forbidden + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_not_logged_user_cannot_modify_existing_region(self): + """Not logged-in user cannot modify existing region + """ + # Create instance + instance = self.factory() + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.put(url, {}, format='json') + + # Assert forbidden code + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_not_logged_user_cannot_delete_existing_region(self): + """Not logged-in user cannot delete existing region + """ + # Create instances + instance = self.factory() + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.delete(url) + + # Assert instance still exists on db + self.assertTrue(self.model.objects.get(id=instance.pk)) + + def test_not_logged_user_cant_list_region(self): + """Not logged-in user can't read region + """ + # Request list + url = reverse(self.endpoint+'list') + response = self.client.get(url) + + # Assert access is forbidden + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_logged_user_can_list_region(self): + """Regular logged-in user can list region + """ + # Create instances + user = TenantUserFactory() + instances = [self.factory() for n in range(random.randint(1,5))] + + # Authenticate user + user.db = 'default' + self.client.force_authenticate(user=user) + + # Request list + url = reverse(self.endpoint+'list') + response = self.client.get(url) + + # Assert access is allowed + self.assertEqual(response.status_code, status.HTTP_200_OK) + + # Assert all instances are returned + self.assertEqual(len(instances), len(response.data['results'])) + + def test_logged_user_can_create_region(self): + """Regular logged-in user can create new region + """ + # Define request data + data = { + 'name': 'country name _test_ data', + 'country': factories.CountryFactory().pk, + } + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + url = reverse(self.endpoint+'list') + response = self.client.post(url, data=data) + + # Assert endpoint returns created status + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + # Assert instance exists on db + self.assertTrue(self.model.objects.get(id=response.data['id'])) + + def test_logged_user_can_modify_existing_region(self): + """Regular logged-in user can modify existing region + """ + # Create instances + instance = self.factory() + + # Define request data + data = { + 'name': 'country name _test_ data', + 'country': factories.CountryFactory().pk, + } + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.put(url, data, format='json') + # Assert endpoint returns OK code + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_logged_user_can_delete_existing_region(self): + """Regular logged-in user can delete existing region + """ + # Create instances + instance = self.factory() + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.delete(url) + + # assert 204 no content + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + # Assert instance not exists anymore on db + self.assertFalse(self.model.objects.filter(id=instance.pk).exists()) + + +class CityViewSetTest(APITestCase): + """City viewset tests + """ + databases = {'default', 'tenants'} + + def setUp(self): + """Tests setup + """ + self.endpoint = 'api-v1:city-' + self.factory = factories.CityFactory + self.model = models.City + + def test_not_logged_user_cannot_create_city(self): + """Not logged-in user cannot create new city + """ + # Query endpoint + url = reverse(self.endpoint+'list') + response = self.client.post(url, data={}) + + # Assert access is forbidden + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_not_logged_user_cannot_modify_existing_city(self): + """Not logged-in user cannot modify existing city + """ + # Create instance + instance = self.factory() + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.put(url, {}, format='json') + + # Assert forbidden code + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_not_logged_user_cannot_delete_existing_city(self): + """Not logged-in user cannot delete existing city + """ + # Create instances + instance = self.factory() + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.delete(url) + + # Assert instance still exists on db + self.assertTrue(self.model.objects.get(id=instance.pk)) + + def test_not_logged_user_cant_list_city(self): + """Not logged-in user can't read city + """ + # Request list + url = reverse(self.endpoint+'list') + response = self.client.get(url) + + # Assert access is forbidden + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_logged_user_can_list_city(self): + """Regular logged-in user can list city + """ + # Create instances + user = TenantUserFactory() + instances = [self.factory() for n in range(random.randint(1,5))] + + # Authenticate user + user.db = 'default' + self.client.force_authenticate(user=user) + + # Request list + url = reverse(self.endpoint+'list') + response = self.client.get(url) + + # Assert access is allowed + self.assertEqual(response.status_code, status.HTTP_200_OK) + + # Assert all instances are returned + self.assertEqual(len(instances), len(response.data['results'])) + + def test_logged_user_can_create_city(self): + """Regular logged-in user can create new city + """ + # Define request data + data = { + 'name': 'country name _test_ data', + 'region': factories.RegionFactory().pk, + } + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + url = reverse(self.endpoint+'list') + response = self.client.post(url, data=data) + # Assert endpoint returns created status + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + # Assert instance exists on db + self.assertTrue(self.model.objects.get(id=response.data['id'])) + + def test_logged_user_can_modify_existing_city(self): + """Regular logged-in user can modify existing citycity + """ + # Create instances + instance = self.factory() + + # Define request data + data = { + 'name': 'country name _test_ data', + 'region': factories.RegionFactory().pk, + } + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.put(url, data, format='json') + + # Assert endpoint returns OK code + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_logged_user_can_delete_existing_city(self): + """Regular logged-in user can delete existing city + """ + # Create instances + instance = self.factory() + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.delete(url) + + # Assert 204 no content + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + # Assert instance doesn't exists on db anymore + self.assertFalse(self.model.objects.filter(id=instance.pk).exists()) + + +class JurisdictionViewSetTest(APITestCase): + """Jurisdiction viewset tests + """ + databases = {'default', 'tenants'} + + def setUp(self): + """Tests setup + """ + self.endpoint = 'api-v1:jurisdiction-' + self.factory = factories.JurisdictionFactory + self.model = models.Jurisdiction + + def test_not_logged_user_cannot_create_jurisdiction(self): + """Not logged-in user cannot create new city + """ + # Query endpoint + url = reverse(self.endpoint+'list') + response = self.client.post(url, data={}) + + # Assert access is forbidden + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_not_logged_user_cannot_modify_existing_jurisdiction(self): + """Not logged-in user cannot modify existing city + """ + # Create instance + instance = self.factory() + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.put(url, {}, format='json') + + # Assert forbidden code + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_not_logged_user_cannot_delete_existing_jurisdiction(self): + """Not logged-in user cannot delete existing city + """ + # Create instances + instance = self.factory() + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.delete(url) + + # Assert instance still exists on db + self.assertTrue(self.model.objects.get(id=instance.pk)) + + def test_not_logged_user_cant_list_jurisdiction(self): + """Not logged-in user can't read city + """ + # Request list + url = reverse(self.endpoint+'list') + response = self.client.get(url) + + # Assert access is forbidden + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_logged_user_can_list_jurisdiction(self): + """Regular logged-in user can list city + """ + # Create instances + user = TenantUserFactory() + instances = [self.factory() for n in range(random.randint(1,5))] + + # Authenticate user + user.db = 'default' + self.client.force_authenticate(user=user) + + # Request list + url = reverse(self.endpoint+'list') + response = self.client.get(url) + + # Assert access is allowed + self.assertEqual(response.status_code, status.HTTP_200_OK) + + # Assert all instances are returned + self.assertEqual(len(instances), len(response.data['results'])) + + def test_logged_user_can_create_jurisdiction(self): + """Regular logged-in user can create new city + """ + # Define request data + data = { + 'name': 'jurisdiction name test data', + 'region': factories.RegionFactory().pk, + } + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + url = reverse(self.endpoint+'list') + response = self.client.post(url, data=data) + + # Assert endpoint returns created status + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + # Assert instance exists on db + self.assertTrue(self.model.objects.get(id=response.data['id'])) + + def test_logged_user_can_modify_existing_jurisdiction(self): + """Regular logged-in user can modify existing citycity + """ + # Create instances + instance = self.factory() + + # Define request data + data = { + 'name': 'jurisdiction name test MOD data', + 'region': factories.RegionFactory().pk, + } + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.put(url, data, format='json') + + # Assert endpoint returns OK code + self.assertEqual(response.status_code, status.HTTP_200_OK) + + # Assert instance has been modified + for key in data: + self.assertEqual(data[key], response.data[key]) + + def test_logged_user_can_delete_existing_jurisdiction(self): + """Regular logged-in user can delete existing city + """ + # Create instances + instance = self.factory() + + # Authenticate user + user = TenantUserFactory() + user.db = 'default' + self.client.force_authenticate(user=user) + + # Query endpoint + url = reverse(self.endpoint+'detail', args=[instance.pk]) + response = self.client.delete(url) + + # Assert 204 no content + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + # Assert instance not exists anymore on db + self.assertFalse(self.model.objects.filter(id=instance.pk).exists()) + diff --git a/geo/views.py b/geo/views.py new file mode 100644 index 0000000..17037b4 --- /dev/null +++ b/geo/views.py @@ -0,0 +1,72 @@ +from rest_framework.response import Response +from tenants.views import CustomViewSet, CustomExternalViewSet +from rest_framework import viewsets, status +from rest_framework.decorators import permission_classes +from legalq.permissions import GlobalAccess + +from . import models +from . import serializers + + +class CountryViewSet(CustomExternalViewSet): + model = models.Country + read_serializer_class = serializers.CountryReadSerializer + write_serializer_class = serializers.CountryWriteSerializer + model_name = 'country' + queryset = models.Country.objects.all() + filterset_fields = ('name', ) + + def retrieve(self, request, *args, **kwargs): + instance = models.Country.objects.filter(pk=kwargs['pk']).first() + if instance is not None: + serializer = self.read_serializer_class(instance) + return Response(serializer.data) + return Response(status=status.HTTP_404_NOT_FOUND) + + permission_classes = [GlobalAccess] + + +class RegionViewSet(viewsets.ReadOnlyModelViewSet): + model = models.Region + serializer_class = serializers.RegionReadSerializer + write_serializer_class = serializers.RegionWriteSerializer + model_name = 'region' + queryset = models.Region.objects.all() + filterset_fields = ('name', 'country__name') + + permission_classes = [GlobalAccess] + + +class ProvinceViewSet(CustomExternalViewSet): + model = models.Province + read_serializer_class = serializers.ProvinceReadSerializer + model_name = 'region' + queryset = models.Province.objects.all() + filterset_fields = ('name', 'region__country__name') + + def retrieve(self, request, *args, **kwargs): + instance = models.Province.objects.filter(pk=kwargs['pk']).first() + if instance is not None: + serializer = self.read_serializer_class(instance) + return Response(serializer.data) + return Response(status=status.HTTP_404_NOT_FOUND) + + permission_classes = [GlobalAccess] + + +class CityViewSet(CustomExternalViewSet): + model = models.City + read_serializer_class = serializers.CityReadSerializer + write_serializer_class = serializers.CityWriteSerializer + model_name = 'city' + queryset = models.City.objects.all() + + def retrieve(self, request, *args, **kwargs): + instance = models.City.objects.filter(pk=kwargs['pk']).first() + if instance is not None: + serializer = self.read_serializer_class(instance) + return Response(serializer.data) + return Response(status=status.HTTP_404_NOT_FOUND) + + permission_classes = [GlobalAccess] + filterset_fields = ('name', 'province__name', 'province__region__name') diff --git a/products/models.py b/products/models.py index df00e84..0e5bdf3 100644 --- a/products/models.py +++ b/products/models.py @@ -30,9 +30,10 @@ class Product(models.Model): update_date = models.DateTimeField('Fecha de actualización de producto', null=True, blank=True) discount = models.DecimalField('Descuento', max_digits=5, decimal_places=2, null=True, blank=True) stock = models.PositiveIntegerField('Stock', null=True) - # tags - # category (main tag) - # identifiers = models.ManyToManyField('ean', null=True, blank=True) # EAN, UPC, MPN ??? + # tags = models.ManyToMany(Tag, null=True, blank=True ) + # category = models.ForeignKey(Tag, null=true) # main tag category + # attributes = models.ManyToMany(Tag, null=True, blank=True ) + identifiers = models.TextField('Identificador único de producto', null=True, blank=True) # internal created = models.DateTimeField('date of creation', auto_now_add=True)