Skip to content

Commit 7f0f4fb

Browse files
committed
Basic script to generate tableschema from model
1 parent 664862f commit 7f0f4fb

File tree

2 files changed

+261
-0
lines changed

2 files changed

+261
-0
lines changed

core/utils/tableschema.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import json
2+
3+
from django.db import models
4+
5+
6+
TYPE_MAPPING = {
7+
"AutoField": "integer",
8+
"BigAutoField": "integer",
9+
"BigIntegerField": "integer",
10+
"BinaryField": "string",
11+
"BooleanField": "boolean",
12+
"CharField": "string",
13+
"DateField": "date",
14+
"DateTimeField": "datetime",
15+
"DecimalField": "number",
16+
"DurationField": "string",
17+
"EmailField": "string",
18+
"FileField": "string",
19+
"FilePathField": "string",
20+
"FloatField": "number",
21+
"IntegerField": "integer",
22+
"GenericIPAddressField": "string",
23+
"NullBooleanField": "boolean",
24+
"PositiveIntegerField": "integer",
25+
"PositiveSmallIntegerField": "integer",
26+
"SlugField": "string",
27+
"SmallIntegerField": "integer",
28+
"TextField": "string",
29+
"TimeField": "time",
30+
"URLField": "string",
31+
"UUIDField": "string",
32+
}
33+
34+
35+
def generate_schema_from_model(model, exclude_fields=[]):
36+
"""
37+
Generate a table schema from a Django model.
38+
The schema is a list of dictionaries, each representing a field in the model.
39+
Each dictionary contains:
40+
- name: the name of the field
41+
- type: the type of the field (CharField, IntegerField, etc.)
42+
- required: whether the field is required (not null and not blank)
43+
- choices: if the field has choices, a list of possible values
44+
"""
45+
schema_name = f"schema-{model._meta.verbose_name_plural.lower()}"
46+
schema_filename = f"{schema_name}.json"
47+
schema = {
48+
"$schema": "https://frictionlessdata.io/schemas/table-schema.json",
49+
"encoding": "utf-8",
50+
"fields": [],
51+
"homepage": "https://github.com/quizanthropocene",
52+
"name": schema_name,
53+
"path": f"https://raw.githubusercontent.com/quizanthropocene/admin-backend/refs/heads/master/data/{schema_filename}", # noqa
54+
}
55+
56+
for field in model._meta.get_fields():
57+
# skip excluded fields
58+
if field.name in exclude_fields:
59+
continue
60+
# skip auto fields
61+
if isinstance(field, models.AutoField):
62+
continue
63+
# skip FK & M2M fields
64+
if field.is_relation:
65+
continue
66+
field_info = {
67+
"name": field.name,
68+
"type": TYPE_MAPPING.get(field.get_internal_type(), "string"),
69+
"required": not (field.null or field.blank),
70+
}
71+
if hasattr(field, "choices") and field.choices:
72+
field_info["choices"] = [choice[0] for choice in field.choices]
73+
schema["fields"].append(field_info)
74+
75+
with open(f"data/{schema_filename}", "w", encoding="utf-8") as f:
76+
json.dump(schema, f, ensure_ascii=False, indent=4)

data/schema-questions.json

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
{
2+
"$schema": "https://frictionlessdata.io/schemas/table-schema.json",
3+
"encoding": "utf-8",
4+
"fields": [
5+
{
6+
"name": "text",
7+
"type": "string",
8+
"required": true
9+
},
10+
{
11+
"name": "hint",
12+
"type": "string",
13+
"required": false
14+
},
15+
{
16+
"name": "type",
17+
"type": "string",
18+
"required": true,
19+
"choices": [
20+
"QCM",
21+
"QCM-RM",
22+
"VF"
23+
]
24+
},
25+
{
26+
"name": "difficulty",
27+
"type": "integer",
28+
"required": true,
29+
"choices": [
30+
0,
31+
1,
32+
2,
33+
3,
34+
4
35+
]
36+
},
37+
{
38+
"name": "language",
39+
"type": "string",
40+
"required": true,
41+
"choices": [
42+
"FRENCH",
43+
"ENGLISH",
44+
"SPANISH",
45+
"ITALIAN",
46+
"GERMAN"
47+
]
48+
},
49+
{
50+
"name": "answer_choice_a",
51+
"type": "string",
52+
"required": false
53+
},
54+
{
55+
"name": "answer_choice_b",
56+
"type": "string",
57+
"required": false
58+
},
59+
{
60+
"name": "answer_choice_c",
61+
"type": "string",
62+
"required": false
63+
},
64+
{
65+
"name": "answer_choice_d",
66+
"type": "string",
67+
"required": false
68+
},
69+
{
70+
"name": "answer_correct",
71+
"type": "string",
72+
"required": false,
73+
"choices": [
74+
"a",
75+
"b",
76+
"c",
77+
"d",
78+
"ab",
79+
"ac",
80+
"ad",
81+
"bc",
82+
"bd",
83+
"cd",
84+
"abc",
85+
"abd",
86+
"acd",
87+
"bcd",
88+
"abcd"
89+
]
90+
},
91+
{
92+
"name": "has_ordered_answers",
93+
"type": "boolean",
94+
"required": true
95+
},
96+
{
97+
"name": "answer_explanation",
98+
"type": "string",
99+
"required": false
100+
},
101+
{
102+
"name": "answer_audio_url",
103+
"type": "string",
104+
"required": false
105+
},
106+
{
107+
"name": "answer_audio_url_text",
108+
"type": "string",
109+
"required": false
110+
},
111+
{
112+
"name": "answer_video_url",
113+
"type": "string",
114+
"required": false
115+
},
116+
{
117+
"name": "answer_video_url_text",
118+
"type": "string",
119+
"required": false
120+
},
121+
{
122+
"name": "answer_source_accessible_url",
123+
"type": "string",
124+
"required": false
125+
},
126+
{
127+
"name": "answer_source_accessible_url_text",
128+
"type": "string",
129+
"required": false
130+
},
131+
{
132+
"name": "answer_source_scientific_url",
133+
"type": "string",
134+
"required": false
135+
},
136+
{
137+
"name": "answer_source_scientific_url_text",
138+
"type": "string",
139+
"required": false
140+
},
141+
{
142+
"name": "answer_book_recommendation",
143+
"type": "string",
144+
"required": false
145+
},
146+
{
147+
"name": "answer_image_url",
148+
"type": "string",
149+
"required": false
150+
},
151+
{
152+
"name": "answer_image_url_text",
153+
"type": "string",
154+
"required": false
155+
},
156+
{
157+
"name": "answer_extra_info",
158+
"type": "string",
159+
"required": false
160+
},
161+
{
162+
"name": "visibility",
163+
"type": "string",
164+
"required": true,
165+
"choices": [
166+
"PUBLIC",
167+
"HIDDEN",
168+
"PRIVATE"
169+
]
170+
},
171+
{
172+
"name": "author_certify_necessary_rights",
173+
"type": "boolean",
174+
"required": true
175+
},
176+
{
177+
"name": "author_agree_commercial_use",
178+
"type": "boolean",
179+
"required": true
180+
}
181+
],
182+
"homepage": "https://github.com/quizanthropocene",
183+
"name": "schema-questions",
184+
"path": "https://raw.githubusercontent.com/quizanthropocene/admin-backend/refs/heads/master/data/schema-questions.json"
185+
}

0 commit comments

Comments
 (0)