Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions core/utils/tableschema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import json

from django.db import models


TYPE_MAPPING = {
"AutoField": "integer",
"BigAutoField": "integer",
"BigIntegerField": "integer",
"BinaryField": "string",
"BooleanField": "boolean",
"CharField": "string",
"DateField": "date",
"DateTimeField": "datetime",
"DecimalField": "number",
"DurationField": "string",
"EmailField": "string",
"FileField": "string",
"FilePathField": "string",
"FloatField": "number",
"IntegerField": "integer",
"GenericIPAddressField": "string",
"NullBooleanField": "boolean",
"PositiveIntegerField": "integer",
"PositiveSmallIntegerField": "integer",
"SlugField": "string",
"SmallIntegerField": "integer",
"TextField": "string",
"TimeField": "time",
"URLField": "string",
"UUIDField": "string",
}


def generate_schema_from_model(model, exclude_fields=[]):
"""
Generate a table schema from a Django model.
The schema is a list of dictionaries, each representing a field in the model.
Each dictionary contains:
- name: the name of the field
- type: the type of the field (CharField, IntegerField, etc.)
- required: whether the field is required (not null and not blank)
- choices: if the field has choices, a list of possible values
"""
schema_name = f"schema-{model._meta.verbose_name_plural.lower()}"
schema_filename = f"{schema_name}.json"
schema = {
"$schema": "https://frictionlessdata.io/schemas/table-schema.json",
"encoding": "utf-8",
"fields": [],
"homepage": "https://github.com/quizanthropocene",
"name": schema_name,
"path": f"https://raw.githubusercontent.com/quizanthropocene/admin-backend/refs/heads/master/data/{schema_filename}", # noqa
}

for field in model._meta.get_fields():
# skip excluded fields
if field.name in exclude_fields:
continue
# skip auto fields
if isinstance(field, models.AutoField):
continue
# skip FK & M2M fields
if field.is_relation:
continue
field_info = {
"name": field.name,
"type": TYPE_MAPPING.get(field.get_internal_type(), "string"),
"required": not (field.null or field.blank),
}
if hasattr(field, "choices") and field.choices:
field_info["choices"] = [choice[0] for choice in field.choices]
schema["fields"].append(field_info)

with open(f"data/{schema_filename}", "w", encoding="utf-8") as f:
json.dump(schema, f, ensure_ascii=False, indent=4)
185 changes: 185 additions & 0 deletions data/schema-questions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
{
"$schema": "https://frictionlessdata.io/schemas/table-schema.json",
"encoding": "utf-8",
"fields": [
{
"name": "text",
"type": "string",
"required": true
},
{
"name": "hint",
"type": "string",
"required": false
},
{
"name": "type",
"type": "string",
"required": true,
"choices": [
"QCM",
"QCM-RM",
"VF"
]
},
{
"name": "difficulty",
"type": "integer",
"required": true,
"choices": [
0,
1,
2,
3,
4
]
},
{
"name": "language",
"type": "string",
"required": true,
"choices": [
"FRENCH",
"ENGLISH",
"SPANISH",
"ITALIAN",
"GERMAN"
]
},
{
"name": "answer_choice_a",
"type": "string",
"required": false
},
{
"name": "answer_choice_b",
"type": "string",
"required": false
},
{
"name": "answer_choice_c",
"type": "string",
"required": false
},
{
"name": "answer_choice_d",
"type": "string",
"required": false
},
{
"name": "answer_correct",
"type": "string",
"required": false,
"choices": [
"a",
"b",
"c",
"d",
"ab",
"ac",
"ad",
"bc",
"bd",
"cd",
"abc",
"abd",
"acd",
"bcd",
"abcd"
]
},
{
"name": "has_ordered_answers",
"type": "boolean",
"required": true
},
{
"name": "answer_explanation",
"type": "string",
"required": false
},
{
"name": "answer_audio_url",
"type": "string",
"required": false
},
{
"name": "answer_audio_url_text",
"type": "string",
"required": false
},
{
"name": "answer_video_url",
"type": "string",
"required": false
},
{
"name": "answer_video_url_text",
"type": "string",
"required": false
},
{
"name": "answer_source_accessible_url",
"type": "string",
"required": false
},
{
"name": "answer_source_accessible_url_text",
"type": "string",
"required": false
},
{
"name": "answer_source_scientific_url",
"type": "string",
"required": false
},
{
"name": "answer_source_scientific_url_text",
"type": "string",
"required": false
},
{
"name": "answer_book_recommendation",
"type": "string",
"required": false
},
{
"name": "answer_image_url",
"type": "string",
"required": false
},
{
"name": "answer_image_url_text",
"type": "string",
"required": false
},
{
"name": "answer_extra_info",
"type": "string",
"required": false
},
{
"name": "visibility",
"type": "string",
"required": true,
"choices": [
"PUBLIC",
"HIDDEN",
"PRIVATE"
]
},
{
"name": "author_certify_necessary_rights",
"type": "boolean",
"required": true
},
{
"name": "author_agree_commercial_use",
"type": "boolean",
"required": true
}
],
"homepage": "https://github.com/quizanthropocene",
"name": "schema-questions",
"path": "https://raw.githubusercontent.com/quizanthropocene/admin-backend/refs/heads/master/data/schema-questions.json"
}
Loading