1
Fork 0
mirror of https://github.com/Steffo99/sophon.git synced 2024-12-22 14:54:22 +00:00

💥 Basically rewrite all database parts from scratch

This commit is contained in:
Steffo 2021-04-08 16:41:43 +02:00
parent 0a215babeb
commit be1a2c0e03
9 changed files with 534 additions and 177 deletions

View file

@ -15,8 +15,8 @@ class ProjectAdmin(CoreAdmin):
""" """
list_display = ( list_display = (
"slug",
"name", "name",
"description",
) )
@ -27,8 +27,62 @@ class DataSourceAdmin(CoreAdmin):
""" """
list_display = ( list_display = (
"pandasdmx_id", "id",
"builtin", "name",
"data_content_type",
"last_sync",
)
fieldsets = (
(
None, {
"fields": (
"id",
"name",
"description",
)
}
),
(
"URLs", {
"fields": (
"url",
"documentation",
)
}
),
(
"API configuration", {
"fields": (
"data_content_type",
"headers",
"resources",
)
}
),
(
"Features supported", {
"fields": (
"supports_agencyscheme",
"supports_categoryscheme",
"supports_codelist",
"supports_conceptscheme",
"supports_data",
"supports_dataflow",
"supports_datastructure",
"supports_provisionagreement",
"supports_preview",
"supports_structurespecific_data",
)
}
),
(
"Syncronization", {
"fields": (
"last_sync",
)
}
)
) )
@ -39,7 +93,6 @@ class DataFlowAdmin(CoreAdmin):
""" """
list_display = ( list_display = (
"sdmx_id", "datasource",
"datasource_id", "id",
"description",
) )

View file

@ -1,39 +1,21 @@
# Generated by Django 3.1.7 on 2021-04-04 23:41 # Generated by Django 3.2 on 2021-04-08 14:36
# Manually edited by @Steffo99
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
import importlib.resources
from .. import models as core_models
def create_builtin_sources(apps, schema_editor): def create_builtin_sources(apps, schema_editor):
""" """
Create in the database the sources that are already built-in in :mod:`pandasdmx`. Create in the database the sources that are already built-in in :mod:`pandasdmx`.
This function is called when performing the migration: see This function is called when performing the migration: see
`this page <https://docs.djangoproject.com/en/3.1/topics/migrations/#data-migrations>`_ for details on how this `this page <https://docs.djangoproject.com/en/3.1/topics/migrations/#data-migrations>`_ for details on how this
works! works!
""" """
DataSource = apps.get_model("core", "DataSource") file = importlib.resources.open_text("sophon.core.migrations", "0001_sources.json")
DataSource.objects.bulk_create([ core_models.DataSource.create_from_sources_json(file=file)
DataSource(pandasdmx_id="ABS", builtin=True), file.close()
DataSource(pandasdmx_id="ESTAT", builtin=True),
DataSource(pandasdmx_id="ECB", builtin=True),
DataSource(pandasdmx_id="ILO", builtin=True),
DataSource(pandasdmx_id="IMF", builtin=True),
DataSource(pandasdmx_id="INEGI", builtin=True),
DataSource(pandasdmx_id="INSEE", builtin=True),
DataSource(pandasdmx_id="ISTAT", builtin=True),
DataSource(pandasdmx_id="LSD", builtin=True),
DataSource(pandasdmx_id="NB", builtin=True),
DataSource(pandasdmx_id="NBB", builtin=True),
DataSource(pandasdmx_id="OECD", builtin=True),
DataSource(pandasdmx_id="SGR", builtin=True),
DataSource(pandasdmx_id="SPC", builtin=True),
DataSource(pandasdmx_id="STAT_EE", builtin=True),
DataSource(pandasdmx_id="UNSD", builtin=True),
DataSource(pandasdmx_id="UNICEF", builtin=True),
DataSource(pandasdmx_id="WB", builtin=True),
DataSource(pandasdmx_id="WB_WDI", builtin=True),
])
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -44,22 +26,52 @@ class Migration(migrations.Migration):
] ]
operations = [ operations = [
migrations.CreateModel(
name='DataFlow',
fields=[
('surrogate_id', models.IntegerField(help_text='Internal id used by Django to identify this DataFlow.', primary_key=True, serialize=False, verbose_name='Surrogate id')),
('id', models.CharField(help_text='Internal string used in SDMX communication to identify the DataFlow.', max_length=64, verbose_name='SDMX id')),
('description', models.TextField(blank=True, help_text='Natural language description of the DataFlow.', verbose_name='Description')),
],
),
migrations.CreateModel( migrations.CreateModel(
name='DataSource', name='DataSource',
fields=[ fields=[
('pandasdmx_id', models.CharField(max_length=16, primary_key=True, serialize=False, verbose_name='Internal pandasdmx source id')), ('id', models.CharField(help_text='Internal id used by PandaSDMX to reference the source.', max_length=16, primary_key=True, serialize=False, verbose_name='PandaSDMX id')),
('builtin', models.BooleanField(verbose_name='If the source is builtin in pandasdmx')), ('name', models.CharField(help_text='Full length name of the data source.', max_length=512, verbose_name='Name')),
('settings', models.JSONField(null=True, verbose_name='Source info to pass to pandasdmx if the source is not builtin')), ('description', models.TextField(blank=True, help_text='Long description of the data source.', verbose_name='Description')),
('url', models.URLField(help_text='The base URL of the SDMX endpoint of the data source.', verbose_name='API URL')),
('documentation', models.URLField(help_text='Documentation URL of the data source.', null=True, verbose_name='Documentation URL')),
('data_content_type', models.CharField(choices=[('JSON', 'JSON'), ('XML', 'XML')], default='XML', help_text='The format in which the API returns its data.', max_length=16, verbose_name='API type')),
('headers', models.JSONField(default=dict, help_text='HTTP headers to attach to every request, as a JSON object.', verbose_name='HTTP Headers')),
('resources', models.JSONField(default=dict, help_text='Unknown and undocumented JSON object.', verbose_name='Resources')),
('supports_agencyscheme', models.BooleanField(default=True, help_text='Whether the data source supports <a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.AgencyScheme">AgencyScheme </a> or not.', verbose_name='Supports AgencyScheme')),
('supports_categoryscheme', models.BooleanField(default=True, help_text='Whether the data source supports <a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.CategoryScheme">CategoryScheme </a> or not.', verbose_name='Supports CategoryScheme')),
('supports_codelist', models.BooleanField(default=True, help_text='Whether the data source supports <a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.CodeList">CodeList </a> or not.', verbose_name='Supports CodeList')),
('supports_conceptscheme', models.BooleanField(default=True, help_text='Whether the data source supports <a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.ConceptScheme">ConceptScheme </a> or not.', verbose_name='Supports ConceptScheme')),
('supports_data', models.BooleanField(default=True, help_text='Whether the data source supports <a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.DataSet">DataSet </a> or not.', verbose_name='Supports DataSet')),
('supports_dataflow', models.BooleanField(default=True, help_text='Whether the data source supports <a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.DataflowDefinition">DataflowDefinition </a> or not.', verbose_name='Supports DataflowDefinition')),
('supports_datastructure', models.BooleanField(default=True, help_text='Whether the data source supports <a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.DataStructureDefinition">CategoryScheme </a> or not.', verbose_name='Supports DataStructureDefinition')),
('supports_provisionagreement', models.BooleanField(default=True, help_text='Whether the data source supports <a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.ProvisionAgreement">CategoryScheme </a> or not.', verbose_name='Supports ProvisionAgreement')),
('supports_preview', models.BooleanField(default=False, help_text='Whether the data source supports <a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.Request.preview_data">previews of data </a> or not.', verbose_name='Supports previews')),
('supports_structurespecific_data', models.BooleanField(default=False, help_text='Whether the data source returns <a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.source.Source">structure-specific data messages </a> or not.', verbose_name='Supports structure-specific data messages')),
('builtin', models.BooleanField(help_text='Whether the source is built-in in PandaSDMX or not.', verbose_name='Builtin')),
('last_sync', models.DateTimeField(help_text='The datetime at which the data flows of this source were last syncronized.', null=True, verbose_name='Last updated')),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='Project', name='Project',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('slug', models.SlugField(help_text='Unique alphanumeric string which identifies the project.', max_length=64, primary_key=True, serialize=False, verbose_name='Slug')),
('name', models.CharField(max_length=512, verbose_name='Project name')), ('name', models.CharField(help_text='The display name of the project.', max_length=512, verbose_name='Name')),
('description', models.CharField(max_length=8192, verbose_name='Project description')), ('description', models.TextField(blank=True, help_text='A brief description of the project, to be displayed inthe overview.', verbose_name='Description')),
('sources', models.ManyToManyField(related_name='used_in', to='core.DataSource')), ('flows', models.ManyToManyField(blank=True, help_text='The DataFlows used in this project.', related_name='used_in', to='core.DataFlow')),
], ],
), ),
migrations.AddField(
model_name='dataflow',
name='datasource',
field=models.ForeignKey(help_text='The DataSource this object belongs to.', on_delete=django.db.models.deletion.RESTRICT, to='core.datasource'),
),
migrations.RunPython(create_builtin_sources) migrations.RunPython(create_builtin_sources)
] ]

View file

@ -0,0 +1,182 @@
[
{
"id": "ABS",
"data_content_type": "JSON",
"url": "https://stat.data.abs.gov.au/sdmx-json",
"name": "Australian Bureau of Statistics",
"documentation": "https://www.abs.gov.au/"
},
{
"id": "ECB",
"resources": {
"data": {
"headers": {}
}
},
"url": "https://sdw-wsrest.ecb.europa.eu/service",
"name": "European Central Bank",
"documentation": "https://www.ecb.europa.eu/stats/ecb_statistics/co-operation_and_standards/sdmx/html/index.en.html",
"supports": {"preview": true}
},
{
"id": "ESTAT",
"documentation": "https://data.un.org/Host.aspx?Content=API",
"url": "https://ec.europa.eu/eurostat/SDMX/diss-web/rest",
"name": "Eurostat",
"supports": {
"agencyscheme": false,
"categoryscheme": false,
"codelist": false,
"conceptscheme": false,
"provisionagreement": false
}
},
{
"id": "ILO",
"name": "International Labor Organization",
"documentation": "https://www.ilo.org/ilostat/",
"url": "https://www.ilo.org/sdmx/rest",
"headers": {
"accept": "application/vnd.sdmx.structurespecificdata+xml;version=2.1"
},
"supports": {
"provisionagreement": false
}
},
{
"id": "IMF",
"url": "https://sdmxcentral.imf.org/ws/public/sdmxapi/rest",
"name": "International Monetary Fund",
"supports": {
"provisionagreement": false
}
},
{
"id": "INEGI",
"url": "https://sdmx.snieg.mx/service/rest",
"name": "Instituto Nacional de Estadística y Geografía (MX)",
"documentation": "https://sdmx.snieg.mx/infrastructure",
"supports": {
"agencyscheme": false,
"provisionagreement": false,
"structure-specific data": true
}
},
{
"id": "INSEE",
"name": "Institut national de la statistique et des études économiques (FR)",
"documentation": "https://www.bdm.insee.fr/bdm2/statique?page=sdmx",
"url": "https://www.bdm.insee.fr/series/sdmx",
"headers": {
"data": {
"Accept": "application/vnd.sdmx.genericdata+xml;version=2.1"
}
},
"supports": {"provisionagreement": false}
},
{
"id": "ISTAT",
"name": "Instituto Nationale di Statistica (IT)",
"documentation": "https://ec.europa.eu/eurostat/web/sdmx-web-services/rest-sdmx-2.1",
"url": "http://sdmx.istat.it/SDMXWS/rest",
"supports": {
"provisionagreement": false,
"structure-specific data": true
}
},
{
"id": "OECD",
"data_content_type": "JSON",
"url": "https://stats.oecd.org/SDMX-JSON",
"documentation": "https://stats.oecd.org/SDMX-JSON/",
"name": "Organisation for Economic Co-operation and Development"
},
{
"id": "NBB",
"data_content_type": "JSON",
"documentation": "https://www.nbb.be/doc/dq/migratie_belgostat/en/nbb_stat-technical-manual.pdf",
"url": "https://stat.nbb.be/sdmx-json",
"name": "National Bank of Belgium"
},
{
"id": "NB",
"name": "Norges Bank (NO)",
"documentation": "https://www.norges-bank.no/en/topics/Statistics/open-data/",
"url": "https://data.norges-bank.no/api",
"supports": {"categoryscheme": false, "structure-specific data": true},
"headers": {
"data": {
"accept": "application/vnd.sdmx.genericdata+xml;version=2.1"
}
}
},
{
"id": "SGR",
"url": "https://registry.sdmx.org/ws/rest",
"name": "SDMX Global Registry",
"documentation": "https://registry.sdmx.org/ws/rest"
},
{
"id": "UNICEF",
"name": "UN International Children's Emergency Fund",
"documentation": "https://data.unicef.org/",
"url": "https://sdmx.data.unicef.org/ws/public/sdmxapi/rest",
"headers": {
"accept": "application/vnd.sdmx.structure+xml;version=2.1"
}
},
{
"id": "SPC",
"name": "Pacific Data Hub",
"documentation":"https://stats.pacificdata.org/?locale=en",
"url": "https://stats-nsi-stable.pacificdata.org/rest",
"supports": {"preview": false, "provisionagreement": false}
},
{
"id": "UNSD",
"name": "United Nations Statistics Division",
"documentation": "https://unstats.un.org/home/",
"url": "https://data.un.org/WS/rest",
"supports": {"preview": true, "provisionagreement": false}
},
{
"id": "WB",
"name": "World Bank World Integrated Trade Solution",
"documentation": "http://wits.worldbank.org",
"url": "http://wits.worldbank.org/API/V1/SDMX/V21/rest",
"supports": {
"agencyscheme": false,
"provisionagreement": false
}
},
{
"id": "WB_WDI",
"name": "World Bank World Development Indicators",
"documentation": "https://datahelpdesk.worldbank.org/knowledgebase/articles/1886701-sdmx-api-queries",
"url": "http://api.worldbank.org/v2/sdmx/rest",
"supports": {
"provisionagreement": false,
"structure-specific data": true
}
},
{
"id": "LSD",
"documentation": "https://osp.stat.gov.lt/rdb-rest",
"url": "https://osp-rs.stat.gov.lt/rest_xml",
"name": "Statistics Lithuania",
"supports": {
"categoryscheme": false,
"codelist": false,
"conceptscheme": false,
"provisionagreement": false
}
},
{
"id": "STAT_EE",
"data_content_type": "JSON",
"documentation": "https://www.stat.ee/sites/default/files/2020-09/API-instructions.pdf",
"url": "http://andmebaas.stat.ee/sdmx-json",
"name": "Statistics Estonia"
}
]

View file

@ -1,43 +0,0 @@
# Generated by Django 3.1.7 on 2021-04-05 16:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='datasource',
name='builtin',
field=models.BooleanField(help_text='Whether the source is builtin in PandaSDMX or not.', verbose_name='Builtin'),
),
migrations.AlterField(
model_name='datasource',
name='pandasdmx_id',
field=models.CharField(help_text='Internal id used by PandaSDMX to reference the source.', max_length=16, primary_key=True, serialize=False, verbose_name='PandaSDMX id'),
),
migrations.AlterField(
model_name='datasource',
name='settings',
field=models.JSONField(help_text='Info parameter to pass to pandasdmx.add_source if the source is not builtin (see https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.add_source).', null=True, verbose_name='Settings'),
),
migrations.AlterField(
model_name='project',
name='description',
field=models.CharField(help_text='A brief description of the project, to be displayed inthe overview.', max_length=8192, verbose_name='Project description'),
),
migrations.AlterField(
model_name='project',
name='name',
field=models.CharField(help_text='The display name of the project.', max_length=512, verbose_name='Project name'),
),
migrations.AlterField(
model_name='project',
name='sources',
field=models.ManyToManyField(help_text='The sources used by this project.', null=True, related_name='used_in', to='core.DataSource'),
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 3.1.7 on 2021-04-06 00:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0002_auto_20210405_1641'),
]
operations = [
migrations.AlterField(
model_name='project',
name='sources',
field=models.ManyToManyField(blank=True, help_text='The sources used by this project.', related_name='used_in', to='core.DataSource'),
),
]

View file

@ -1,24 +0,0 @@
# Generated by Django 3.1.7 on 2021-04-06 20:55
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('core', '0003_auto_20210406_0044'),
]
operations = [
migrations.CreateModel(
name='DataFlow',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sdmx_id', models.CharField(help_text='Internal string used in SDMX communication to identify the DataFlow.', max_length=64, verbose_name='SDMX id')),
('last_update', models.DateTimeField(help_text='The datetime at which the properties of this DataFlow were last updated.', verbose_name='Last updated')),
('description', models.CharField(help_text='Natural language description of the DataFlow.', max_length=8192, verbose_name='DataFlow description')),
('datasource_id', models.ForeignKey(help_text='The DataSource this object belongs to.', on_delete=django.db.models.deletion.RESTRICT, to='core.datasource')),
],
),
]

View file

@ -1,18 +0,0 @@
# Generated by Django 3.2 on 2021-04-07 17:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_dataflow'),
]
operations = [
migrations.AlterField(
model_name='dataflow',
name='last_update',
field=models.DateTimeField(auto_now=True, help_text='The datetime at which the properties of this DataFlow were last updated.', verbose_name='Last updated'),
),
]

View file

@ -1,7 +1,10 @@
from django.db import models from django.db import models
from django.core import validators
import pandas import pandas
import pandasdmx import pandasdmx
import pandasdmx.message import pandasdmx.message
import typing as t
import json
class DataSource(models.Model): class DataSource(models.Model):
@ -16,24 +19,203 @@ class DataSource(models.Model):
method. method.
""" """
pandasdmx_id = models.CharField( id = models.CharField(
"PandaSDMX id", "PandaSDMX id",
help_text="Internal id used by PandaSDMX to reference the source.", help_text="Internal id used by PandaSDMX to reference the source.",
max_length=16, max_length=16,
primary_key=True, primary_key=True,
) )
name = models.CharField(
"Name",
help_text="Full length name of the data source.",
max_length=512,
)
description = models.TextField(
"Description",
help_text="Long description of the data source.",
blank=True,
)
url = models.URLField(
"API URL",
help_text="The base URL of the SDMX endpoint of the data source."
)
documentation = models.URLField(
"Documentation URL",
help_text="Documentation URL of the data source.",
null=True,
)
data_content_type = models.CharField(
"API type",
help_text="The format in which the API returns its data.",
choices=[
("JSON", "JSON"),
("XML", "XML"),
],
default="XML",
max_length=16,
)
headers = models.JSONField(
"HTTP Headers",
help_text="HTTP headers to attach to every request, as a JSON object.",
default=dict,
)
resources = models.JSONField(
"Resources",
help_text="Unknown and undocumented JSON object.",
default=dict,
)
supports_agencyscheme = models.BooleanField(
"Supports AgencyScheme",
help_text='Whether the data source supports '
'<a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.AgencyScheme">'
'AgencyScheme '
'</a> or not.',
default=True,
)
supports_categoryscheme = models.BooleanField(
"Supports CategoryScheme",
help_text='Whether the data source supports '
'<a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.CategoryScheme">'
'CategoryScheme '
'</a> or not.',
default=True,
)
supports_codelist = models.BooleanField(
"Supports CodeList",
help_text='Whether the data source supports '
'<a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.CodeList">'
'CodeList '
'</a> or not.',
default=True,
)
supports_conceptscheme = models.BooleanField(
"Supports ConceptScheme",
help_text='Whether the data source supports '
'<a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.ConceptScheme">'
'ConceptScheme '
'</a> or not.',
default=True,
)
supports_data = models.BooleanField(
"Supports DataSet",
help_text='Whether the data source supports '
'<a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.DataSet">'
'DataSet '
'</a> or not.',
default=True,
)
supports_dataflow = models.BooleanField(
"Supports DataflowDefinition",
help_text='Whether the data source supports '
'<a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.DataflowDefinition">'
'DataflowDefinition '
'</a> or not.',
default=True,
)
supports_datastructure = models.BooleanField(
"Supports DataStructureDefinition",
help_text='Whether the data source supports '
'<a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.DataStructureDefinition">'
'CategoryScheme '
'</a> or not.',
default=True,
)
supports_provisionagreement = models.BooleanField(
"Supports ProvisionAgreement",
help_text='Whether the data source supports '
'<a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.model.ProvisionAgreement">'
'CategoryScheme '
'</a> or not.',
default=True,
)
supports_preview = models.BooleanField(
"Supports previews",
help_text='Whether the data source supports '
'<a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.Request.preview_data">'
'previews of data '
'</a> or not.',
default=False,
)
supports_structurespecific_data = models.BooleanField(
"Supports structure-specific data messages",
help_text='Whether the data source returns '
'<a href="https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.source.Source">'
'structure-specific data messages '
'</a> or not.',
default=False,
)
def supports_dict(self) -> dict:
return {
"agencyscheme": self.supports_agencyscheme,
"categoryscheme": self.supports_categoryscheme,
"codelist": self.supports_codelist,
"conceptscheme": self.supports_conceptscheme,
"data": self.supports_data,
"dataflow": self.supports_dataflow,
"datastructure": self.supports_datastructure,
"provisionagreement": self.supports_provisionagreement,
"preview": self.supports_preview,
"structure-specific data": self.supports_structurespecific_data,
}
def info_dict(self) -> dict:
return {
"id": self.id,
"name": self.name,
"data_content_type": self.data_content_type,
"url": self.url,
"documentation": self.documentation,
"supports": self.supports_dict(),
"headers": self.headers,
"resources": self.resources,
}
builtin = models.BooleanField( builtin = models.BooleanField(
"Builtin", "Builtin",
help_text="Whether the source is builtin in PandaSDMX or not.", help_text="Whether the source is built-in in PandaSDMX or not.",
)
settings = models.JSONField(
"Settings",
help_text="Info parameter to pass to pandasdmx.add_source if the source is not builtin "
"(see https://pandasdmx.readthedocs.io/en/latest/api.html#pandasdmx.add_source).",
null=True
) )
@classmethod
def create_from_sources_json(cls, file: t.TextIO):
j_sources: list = json.load(file)
for j_source in j_sources:
# Flatten supports
if supports := j_source.get("supports"):
del j_source["supports"]
for key, value in supports.items():
if key == "structure-specific data":
j_source["supports_structurespecific_data"] = value
else:
j_source[f"supports_{key}"] = value
cls.objects.update_or_create(
id=j_source["id"],
defaults={
**j_source,
"builtin": True,
}
)
def to_pandasdmx_source(self) -> pandasdmx.source.Source: def to_pandasdmx_source(self) -> pandasdmx.source.Source:
""" """
Convert the :class:`.DataSource` to a :class:`pandasdmx.source.Source`\\ . Convert the :class:`.DataSource` to a :class:`pandasdmx.source.Source`\\ .
@ -42,7 +224,7 @@ class DataSource(models.Model):
.. todo:: :func:`.to_pandasdmx` does not currently support non :attr:`.builtin` sources. .. todo:: :func:`.to_pandasdmx` does not currently support non :attr:`.builtin` sources.
""" """
return pandasdmx.source.sources[self.pandasdmx_id] return pandasdmx.source.sources[self.id]
def to_pandasdmx_request(self) -> pandasdmx.Request: def to_pandasdmx_request(self) -> pandasdmx.Request:
""" """
@ -52,6 +234,12 @@ class DataSource(models.Model):
""" """
return pandasdmx.Request(source=self.to_pandasdmx_source().id) return pandasdmx.Request(source=self.to_pandasdmx_source().id)
last_sync = models.DateTimeField(
"Last updated",
help_text="The datetime at which the data flows of this source were last syncronized.",
null=True,
)
def request_flows(self) -> tuple[pandas.Series, pandas.Series]: def request_flows(self) -> tuple[pandas.Series, pandas.Series]:
""" """
Retrieve all available dataflows and datastructures as two :class:`pandas.Series`\\ . Retrieve all available dataflows and datastructures as two :class:`pandas.Series`\\ .
@ -71,7 +259,7 @@ class DataSource(models.Model):
return flows, structs return flows, structs
def __str__(self): def __str__(self):
return self.pandasdmx_id return self.id
class DataFlow(models.Model): class DataFlow(models.Model):
@ -81,30 +269,32 @@ class DataFlow(models.Model):
See `this page <https://ec.europa.eu/eurostat/online-help/redisstat-admin/en/TECH_A_main/>`_ for more details. See `this page <https://ec.europa.eu/eurostat/online-help/redisstat-admin/en/TECH_A_main/>`_ for more details.
""" """
datasource_id = models.ForeignKey( surrogate_id = models.IntegerField(
"Surrogate id",
help_text="Internal id used by Django to identify this DataFlow.",
primary_key=True,
)
datasource = models.ForeignKey(
DataSource, DataSource,
help_text="The DataSource this object belongs to.", help_text="The DataSource this object belongs to.",
on_delete=models.RESTRICT, on_delete=models.RESTRICT,
) )
sdmx_id = models.CharField(
id = models.CharField(
"SDMX id", "SDMX id",
help_text="Internal string used in SDMX communication to identify the DataFlow.", help_text="Internal string used in SDMX communication to identify the DataFlow.",
max_length=64, max_length=64,
) )
last_update = models.DateTimeField(
"Last updated",
help_text="The datetime at which the properties of this DataFlow were last updated.",
auto_now=True,
)
description = models.CharField( description = models.TextField(
"DataFlow description", "Description",
help_text="Natural language description of the DataFlow.", help_text="Natural language description of the DataFlow.",
max_length=8192, blank=True,
) )
def __str__(self): def __str__(self):
return f"{self.datasource_id} | {self.sdmx_id} | {self.description}" return f"[{self.datasource}] {self.id}"
class Project(models.Model): class Project(models.Model):
@ -113,23 +303,31 @@ class Project(models.Model):
hypothesis. hypothesis.
""" """
slug = models.SlugField(
"Slug",
help_text="Unique alphanumeric string which identifies the project.",
max_length=64,
primary_key=True,
)
name = models.CharField( name = models.CharField(
"Project name", "Name",
help_text="The display name of the project.", help_text="The display name of the project.",
max_length=512, max_length=512,
) )
description = models.CharField(
"Project description", description = models.TextField(
"Description",
help_text="A brief description of the project, to be displayed inthe overview.", help_text="A brief description of the project, to be displayed inthe overview.",
max_length=8192, blank=True,
) )
sources = models.ManyToManyField( flows = models.ManyToManyField(
DataSource, DataFlow,
help_text="The sources used by this project.", help_text="The DataFlows used in this project.",
related_name="used_in", related_name="used_in",
blank=True, blank=True,
) )
def __str__(self): def __str__(self):
return self.name return self.slug

View file

@ -10,9 +10,25 @@ class DataSourceSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.DataSource model = models.DataSource
fields = [ fields = [
"pandasdmx_id", "id",
"name",
"description",
"url",
"documentation",
"data_content_type",
"headers",
"supports_agencyscheme",
"supports_categoryscheme",
"supports_codelist",
"supports_conceptscheme",
"supports_data",
"supports_dataflow",
"supports_datastructure",
"supports_provisionagreement",
"supports_preview",
"supports_structurespecific_data",
"builtin", "builtin",
"settings", "last_sync",
] ]
@ -24,10 +40,9 @@ class DataFlowSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.DataFlow model = models.DataFlow
fields = [ fields = [
"surrogate_id",
"datasource",
"id", "id",
"datasource_id",
"sdmx_id",
"last_update",
"description", "description",
] ]
@ -40,8 +55,8 @@ class ProjectSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.Project model = models.Project
fields = [ fields = [
"id", "slug",
"name", "name",
"description", "description",
"sources", "flows",
] ]