python-datamodel 0.8.13__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- datamodel/__init__.py +13 -0
- datamodel/abstract.py +337 -0
- datamodel/aliases/__init__.py +26 -0
- datamodel/base.py +182 -0
- datamodel/converters.c +40958 -0
- datamodel/converters.cp313-win_amd64.pyd +0 -0
- datamodel/exceptions.c +13455 -0
- datamodel/exceptions.cp313-win_amd64.pyd +0 -0
- datamodel/fields.cp313-win_amd64.pyd +0 -0
- datamodel/fields.cpp +17289 -0
- datamodel/functions.cp313-win_amd64.pyd +0 -0
- datamodel/functions.cpp +8940 -0
- datamodel/jsonld/__init__.py +45 -0
- datamodel/jsonld/models.py +500 -0
- datamodel/libs/__init__.py +0 -0
- datamodel/libs/mapping.c +15142 -0
- datamodel/libs/mapping.cp313-win_amd64.pyd +0 -0
- datamodel/libs/mutables.py +128 -0
- datamodel/models.py +787 -0
- datamodel/parsers/__init__.py +0 -0
- datamodel/parsers/encoders.py +15 -0
- datamodel/parsers/json.cp313-win_amd64.pyd +0 -0
- datamodel/parsers/json.cpp +13008 -0
- datamodel/profiler.py +21 -0
- datamodel/py.typed +0 -0
- datamodel/types.c +7165 -0
- datamodel/types.cp313-win_amd64.pyd +0 -0
- datamodel/validation.cp313-win_amd64.pyd +0 -0
- datamodel/validation.cpp +14238 -0
- datamodel/version.py +13 -0
- python_datamodel-0.8.13.dist-info/LICENSE +29 -0
- python_datamodel-0.8.13.dist-info/METADATA +316 -0
- python_datamodel-0.8.13.dist-info/RECORD +35 -0
- python_datamodel-0.8.13.dist-info/WHEEL +5 -0
- python_datamodel-0.8.13.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from .models import (
|
|
2
|
+
URL,
|
|
3
|
+
JobTitle,
|
|
4
|
+
PostalAddress,
|
|
5
|
+
GeoCoordinates,
|
|
6
|
+
Person,
|
|
7
|
+
ImageObject,
|
|
8
|
+
Recipe,
|
|
9
|
+
NutritionInformation,
|
|
10
|
+
Organization,
|
|
11
|
+
AggregateRating,
|
|
12
|
+
Rating,
|
|
13
|
+
Product,
|
|
14
|
+
Review,
|
|
15
|
+
VideoObject,
|
|
16
|
+
AdministrativeArea,
|
|
17
|
+
Audience,
|
|
18
|
+
Place,
|
|
19
|
+
QuantitativeValue,
|
|
20
|
+
MonetaryAmount,
|
|
21
|
+
JobPosting,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
JSON_MODEL_MAP = {
|
|
25
|
+
"URL": URL,
|
|
26
|
+
"JobTitle": JobTitle,
|
|
27
|
+
"PostalAddress": PostalAddress,
|
|
28
|
+
"GeoCoordinates": GeoCoordinates,
|
|
29
|
+
"Person": Person,
|
|
30
|
+
"ImageObject": ImageObject,
|
|
31
|
+
"Recipe": Recipe,
|
|
32
|
+
"NutritionInformation": NutritionInformation,
|
|
33
|
+
"Organization": Organization,
|
|
34
|
+
"AggregateRating": AggregateRating,
|
|
35
|
+
"Rating": Rating,
|
|
36
|
+
"Product": Product,
|
|
37
|
+
"Review": Review,
|
|
38
|
+
"VideoObject": VideoObject,
|
|
39
|
+
"AdministrativeArea": AdministrativeArea,
|
|
40
|
+
"Audience": Audience,
|
|
41
|
+
"Place": Place,
|
|
42
|
+
"QuantitativeValue": QuantitativeValue,
|
|
43
|
+
"MonetaryAmount": MonetaryAmount,
|
|
44
|
+
"JobPosting": JobPosting,
|
|
45
|
+
}
|
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
from typing import Any, List, Optional, Union
|
|
2
|
+
from html import escape
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from ..base import BaseModel, Field, register_renderer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class URL(BaseModel):
|
|
8
|
+
"""
|
|
9
|
+
Corresponds to the JSON-LD "URL" type.
|
|
10
|
+
https://schema.org/URL
|
|
11
|
+
"""
|
|
12
|
+
url: str
|
|
13
|
+
|
|
14
|
+
class Meta:
|
|
15
|
+
schema_type: str = "URL"
|
|
16
|
+
|
|
17
|
+
class JobTitle(BaseModel):
|
|
18
|
+
"""
|
|
19
|
+
The job title of the person (for example, Financial Manager).
|
|
20
|
+
http://schema.org/jobTitle
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
"jobTitle": {
|
|
24
|
+
"@type": "DefinedTerm",
|
|
25
|
+
"inDefinedTermSet": "https://targetjobs.co.uk/careers-advice/job-descriptions",
|
|
26
|
+
"termCode": "277133-aid-workerhumanitarian-worker-job-description",
|
|
27
|
+
"name": "Aid worker/humanitarian worker",
|
|
28
|
+
"url": "https://targetjobs.co.uk/careers-advice/job-descriptions/277133-aid-workerhumanitarian-worker-job-description"
|
|
29
|
+
}
|
|
30
|
+
""" # noqa
|
|
31
|
+
name: str
|
|
32
|
+
url: str
|
|
33
|
+
inDefinedTermSet: str
|
|
34
|
+
termCode: str
|
|
35
|
+
|
|
36
|
+
class Meta:
|
|
37
|
+
schema_type: str = "JobTitle"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class PostalAddress(BaseModel):
|
|
41
|
+
"""
|
|
42
|
+
Corresponds to the JSON-LD "Address" type.
|
|
43
|
+
https://schema.org/PostalAddress
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
"address": {
|
|
47
|
+
"@type": "PostalAddress",
|
|
48
|
+
"addressLocality": "Seattle",
|
|
49
|
+
"addressRegion": "WA",
|
|
50
|
+
"postalCode": "98052",
|
|
51
|
+
"streetAddress": "20341 Whitworth Institute 405 N. Whitworth"
|
|
52
|
+
}
|
|
53
|
+
"""
|
|
54
|
+
streetAddress: str
|
|
55
|
+
addressLocality: str
|
|
56
|
+
addressRegion: str
|
|
57
|
+
postalCode: str
|
|
58
|
+
addressCountry: str
|
|
59
|
+
postOfficeBoxNumber: Optional[str]
|
|
60
|
+
telephone: Optional[str]
|
|
61
|
+
|
|
62
|
+
class Meta:
|
|
63
|
+
schema_type: str = "PostalAddress"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class GeoCoordinates(BaseModel):
|
|
67
|
+
"""
|
|
68
|
+
Corresponds to the JSON-LD "GeoCoordinates" type.
|
|
69
|
+
https://schema.org/GeoCoordinates
|
|
70
|
+
|
|
71
|
+
Properties:
|
|
72
|
+
latitude: The latitude of a location. For example 37.42242 (WGS 84).
|
|
73
|
+
longitude: The longitude of a location. For example -122.08585 (WGS 84).
|
|
74
|
+
elevation: The elevation of a location (WGS 84).
|
|
75
|
+
Values may be of the form 'NUMBER UNIT_OF_MEASUREMENT' (e.g., '1,000 m')
|
|
76
|
+
while numbers alone should be assumed to be a value in meters.
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
"geo": {
|
|
80
|
+
"@type": "GeoCoordinates",
|
|
81
|
+
"latitude": "47.603",
|
|
82
|
+
"longitude": "-122.329"
|
|
83
|
+
}
|
|
84
|
+
"""
|
|
85
|
+
latitude: float
|
|
86
|
+
longitude: float
|
|
87
|
+
elevation: Optional[float]
|
|
88
|
+
|
|
89
|
+
class Meta:
|
|
90
|
+
schema_type: str = "GeoCoordinates"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class Person(BaseModel):
|
|
94
|
+
"""
|
|
95
|
+
Corresponds to the JSON-LD "Person" type.
|
|
96
|
+
https://schema.org/Person
|
|
97
|
+
|
|
98
|
+
example:
|
|
99
|
+
{
|
|
100
|
+
"@context": "http://schema.org/",
|
|
101
|
+
"@type": "Person",
|
|
102
|
+
"name": "Jane Doe",
|
|
103
|
+
"jobTitle": "Professor",
|
|
104
|
+
"telephone": "(425) 123-4567",
|
|
105
|
+
"url": "http://www.janedoe.com"
|
|
106
|
+
}
|
|
107
|
+
"""
|
|
108
|
+
name: str = Field(required=True)
|
|
109
|
+
image: Union["ImageObject", str, None] = None
|
|
110
|
+
jobTitle: Optional[JobTitle]
|
|
111
|
+
telephone: Optional[str] = None
|
|
112
|
+
url: Optional[str] = None
|
|
113
|
+
sameAs: Optional[str] = None
|
|
114
|
+
|
|
115
|
+
class Meta:
|
|
116
|
+
schema_type: str = "Person"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class ImageObject(BaseModel):
|
|
120
|
+
"""
|
|
121
|
+
Corresponds to the JSON-LD "ImageObject" type.
|
|
122
|
+
https://schema.org/ImageObject
|
|
123
|
+
"""
|
|
124
|
+
name: str
|
|
125
|
+
url: str = Field(required=True)
|
|
126
|
+
width: int = 0
|
|
127
|
+
height: int = 0
|
|
128
|
+
caption: str = ""
|
|
129
|
+
|
|
130
|
+
class Meta:
|
|
131
|
+
schema_type: str = "ImageObject"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class Recipe(BaseModel):
|
|
135
|
+
"""
|
|
136
|
+
Corresponds to the JSON-LD "Recipe" type.
|
|
137
|
+
https://schema.org/Recipe
|
|
138
|
+
"""
|
|
139
|
+
name: str
|
|
140
|
+
image: Union["ImageObject", str, None] = None
|
|
141
|
+
datePublished: Optional[str] = None
|
|
142
|
+
description: Optional[str] = None
|
|
143
|
+
prepTime: Optional[str] = None
|
|
144
|
+
cookTime: Optional[str] = None
|
|
145
|
+
totalTime: Optional[str] = None
|
|
146
|
+
recipeIngredient: List[str] = Field(default_factory=list)
|
|
147
|
+
recipeInstructions: List[Any] = Field(default_factory=list)
|
|
148
|
+
recipeCategory: List[str] = Field(default_factory=list)
|
|
149
|
+
recipeCuisine: List[str] = Field(default_factory=list)
|
|
150
|
+
|
|
151
|
+
class Meta:
|
|
152
|
+
schema_type: str = "Recipe"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class NutritionInformation(BaseModel):
|
|
156
|
+
"""
|
|
157
|
+
Example of a NutritionInformation data model.
|
|
158
|
+
"nutrition": {
|
|
159
|
+
"@type": "NutritionInformation",
|
|
160
|
+
"calories": "512.2 calories",
|
|
161
|
+
"carbohydrateContent": "67.8 g",
|
|
162
|
+
"cholesterolContent": "30.5 mg",
|
|
163
|
+
"fatContent": "26.7 g",
|
|
164
|
+
"fiberContent": "5 g",
|
|
165
|
+
"proteinContent": "3.6 g",
|
|
166
|
+
"saturatedFatContent": "11.1 g",
|
|
167
|
+
"servingSize": null,
|
|
168
|
+
"sodiumContent": "240.8 mg",
|
|
169
|
+
"sugarContent": "40.3 g",
|
|
170
|
+
"transFatContent": null,
|
|
171
|
+
"unsaturatedFatContent": null
|
|
172
|
+
},
|
|
173
|
+
"""
|
|
174
|
+
calories: str
|
|
175
|
+
carbohydrateContent: str
|
|
176
|
+
cholesterolContent: str
|
|
177
|
+
fatContent: str
|
|
178
|
+
fiberContent: str
|
|
179
|
+
proteinContent: str
|
|
180
|
+
saturatedFatContent: str
|
|
181
|
+
servingSize: str
|
|
182
|
+
sodiumContent: str
|
|
183
|
+
sugarContent: str
|
|
184
|
+
transFatContent: str
|
|
185
|
+
unsaturatedFatContent: str
|
|
186
|
+
|
|
187
|
+
class Meta:
|
|
188
|
+
schema_type: str = "NutritionInformation"
|
|
189
|
+
|
|
190
|
+
class Organization(BaseModel):
|
|
191
|
+
name: str = Field(required=True)
|
|
192
|
+
url: str
|
|
193
|
+
sameAs: Optional[List[str]] = None
|
|
194
|
+
logo: Optional[ImageObject] = None
|
|
195
|
+
|
|
196
|
+
class Meta:
|
|
197
|
+
schema_type = "Organization"
|
|
198
|
+
|
|
199
|
+
class Rating(BaseModel):
|
|
200
|
+
"""
|
|
201
|
+
Corresponds to the JSON-LD "Rating" type.
|
|
202
|
+
https://schema.org/Rating
|
|
203
|
+
"""
|
|
204
|
+
ratingValue: str = Field(required=True)
|
|
205
|
+
bestRating: str
|
|
206
|
+
worstRating: str
|
|
207
|
+
|
|
208
|
+
class Meta:
|
|
209
|
+
schema_type = "Rating"
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class AggregateRating(BaseModel):
|
|
213
|
+
"""
|
|
214
|
+
Corresponds to the JSON-LD "AggregateRating" type.
|
|
215
|
+
https://schema.org/AggregateRating
|
|
216
|
+
"""
|
|
217
|
+
ratingValue: str = Field(required=True)
|
|
218
|
+
reviewCount: str
|
|
219
|
+
|
|
220
|
+
class Meta:
|
|
221
|
+
schema_type = "AggregateRating"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class Product(BaseModel):
|
|
225
|
+
"""
|
|
226
|
+
Corresponds to the JSON-LD "Product" type.
|
|
227
|
+
https://schema.org/Product
|
|
228
|
+
|
|
229
|
+
Example:
|
|
230
|
+
{
|
|
231
|
+
"@context": "https://schema.org",
|
|
232
|
+
"@type": "Product",
|
|
233
|
+
"aggregateRating": {
|
|
234
|
+
"@type": "AggregateRating",
|
|
235
|
+
"ratingValue": "3.5",
|
|
236
|
+
"reviewCount": "11"
|
|
237
|
+
},
|
|
238
|
+
"description": "0.7 cubic feet countertop microwave.",
|
|
239
|
+
"name": "Kenmore White 17\" Microwave",
|
|
240
|
+
"image": "kenmore-microwave-17in.jpg",
|
|
241
|
+
"offers": {
|
|
242
|
+
"@type": "Offer",
|
|
243
|
+
"availability": "https://schema.org/InStock",
|
|
244
|
+
"price": "55.00",
|
|
245
|
+
"priceCurrency": "USD"
|
|
246
|
+
},
|
|
247
|
+
}
|
|
248
|
+
"""
|
|
249
|
+
name: str = Field(required=True)
|
|
250
|
+
brand: str
|
|
251
|
+
manufacturer: str
|
|
252
|
+
material: str
|
|
253
|
+
model: str
|
|
254
|
+
award: str
|
|
255
|
+
category: str
|
|
256
|
+
gtin: str
|
|
257
|
+
sku: str
|
|
258
|
+
aggregateRating: Optional[AggregateRating]
|
|
259
|
+
|
|
260
|
+
class Meta:
|
|
261
|
+
schema_type = "Product"
|
|
262
|
+
|
|
263
|
+
class Review(BaseModel):
|
|
264
|
+
"""
|
|
265
|
+
Corresponds to the JSON-LD "Review" type.
|
|
266
|
+
https://schema.org/Review
|
|
267
|
+
|
|
268
|
+
Example:
|
|
269
|
+
{
|
|
270
|
+
"@type": "Review",
|
|
271
|
+
"datePublished": "2009-10-15T16:20:23.473Z",
|
|
272
|
+
"reviewBody": "My words from 2008 still hold true today.",
|
|
273
|
+
"reviewRating": {
|
|
274
|
+
"@type": "Rating",
|
|
275
|
+
"worstRating": "1",
|
|
276
|
+
"bestRating": "5",
|
|
277
|
+
"ratingValue": 5
|
|
278
|
+
},
|
|
279
|
+
"author": {
|
|
280
|
+
"@type": "Person",
|
|
281
|
+
"name": "RCLYMA",
|
|
282
|
+
"image": null,
|
|
283
|
+
"sameAs": "https://www.allrecipes.com/cook/575406/"
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
"""
|
|
287
|
+
itemReviewed: Optional[Product]
|
|
288
|
+
reviewRating: Rating
|
|
289
|
+
reviewBody: str = Field(default='')
|
|
290
|
+
author: Person
|
|
291
|
+
datePublished: datetime
|
|
292
|
+
publisher: Organization
|
|
293
|
+
|
|
294
|
+
class Meta:
|
|
295
|
+
schema_type = "Review"
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class VideoObject(BaseModel):
|
|
299
|
+
"""
|
|
300
|
+
Corresponds to the JSON-LD "VideoObject" type.
|
|
301
|
+
https://schema.org/VideoObject
|
|
302
|
+
|
|
303
|
+
Example:
|
|
304
|
+
"video": {
|
|
305
|
+
"@context": "http://schema.org",
|
|
306
|
+
"@type": "VideoObject",
|
|
307
|
+
"name": "Apple Pie by Grandma Ople",
|
|
308
|
+
"description": "Learn how to make Grandma Ople's apple pie.",
|
|
309
|
+
"uploadDate": "2012-05-09T09:07:12.148Z",
|
|
310
|
+
"duration": "PT4M4.744S",
|
|
311
|
+
"thumbnailUrl": "https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fcf-images.us-east-1.prod.boltdns.net%2Fv1%2Fstatic%2F1033249144001%2F571fbd8d-66c7-4521-b002-cbb53ace86e9%2Ff253b5d1-edaf-4dc7-8f0c-954a4259d97f%2F160x90%2Fmatch%2Fimage.jpg",
|
|
312
|
+
"publisher": {
|
|
313
|
+
"@type": "Organization",
|
|
314
|
+
"name": "Allrecipes",
|
|
315
|
+
"url": "https://www.allrecipes.com",
|
|
316
|
+
"logo": {
|
|
317
|
+
"@type": "ImageObject",
|
|
318
|
+
"url": "https://www.allrecipes.com/img/logo.png",
|
|
319
|
+
"width": 209,
|
|
320
|
+
"height": 60
|
|
321
|
+
},
|
|
322
|
+
"sameAs": [
|
|
323
|
+
"https://www.facebook.com/allrecipes",
|
|
324
|
+
"https://twitter.com/Allrecipes",
|
|
325
|
+
"https://www.pinterest.com/allrecipes/",
|
|
326
|
+
"https://www.instagram.com/allrecipes/"
|
|
327
|
+
]
|
|
328
|
+
},
|
|
329
|
+
"embedUrl": "https://players.brightcove.net/1033249144001/default_default/index.html?videoId=1629100183001"
|
|
330
|
+
}
|
|
331
|
+
""" # noqa
|
|
332
|
+
name: str = Field(required=True)
|
|
333
|
+
description: str = ''
|
|
334
|
+
uploadDate: datetime
|
|
335
|
+
duration: str
|
|
336
|
+
thumbnailUrl: str
|
|
337
|
+
publisher: Organization
|
|
338
|
+
embedUrl: str
|
|
339
|
+
|
|
340
|
+
class Meta:
|
|
341
|
+
schema_type = "VideoObject"
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class AdministrativeArea(BaseModel):
|
|
345
|
+
"""
|
|
346
|
+
Corresponds to the JSON-LD "AdministrativeArea" type.
|
|
347
|
+
https://schema.org/AdministrativeArea
|
|
348
|
+
"""
|
|
349
|
+
name: str = Field(required=True)
|
|
350
|
+
geo: Optional[GeoCoordinates]
|
|
351
|
+
branchCode: Optional[str]
|
|
352
|
+
|
|
353
|
+
class Meta:
|
|
354
|
+
schema_type: str = "AdministrativeArea"
|
|
355
|
+
|
|
356
|
+
class Audience(BaseModel):
|
|
357
|
+
"""
|
|
358
|
+
Corresponds to the JSON-LD "Audience" type.
|
|
359
|
+
https://schema.org/Audience
|
|
360
|
+
"""
|
|
361
|
+
name: str = Field(required=True)
|
|
362
|
+
audienceType: str
|
|
363
|
+
geographicArea: Optional[AdministrativeArea]
|
|
364
|
+
|
|
365
|
+
class Meta:
|
|
366
|
+
schema_type: str = "Audience"
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
class Place(BaseModel):
|
|
370
|
+
address: PostalAddress
|
|
371
|
+
|
|
372
|
+
class Meta:
|
|
373
|
+
schema_type = "Place"
|
|
374
|
+
|
|
375
|
+
class QuantitativeValue(BaseModel):
|
|
376
|
+
value: float
|
|
377
|
+
unitText: str
|
|
378
|
+
|
|
379
|
+
class Meta:
|
|
380
|
+
schema_type = "QuantitativeValue"
|
|
381
|
+
|
|
382
|
+
class MonetaryAmount(BaseModel):
|
|
383
|
+
currency: Union[str, float]
|
|
384
|
+
value: QuantitativeValue
|
|
385
|
+
|
|
386
|
+
class Meta:
|
|
387
|
+
schema_type = "MonetaryAmount"
|
|
388
|
+
|
|
389
|
+
class JobPosting(BaseModel):
|
|
390
|
+
title: str
|
|
391
|
+
description: str
|
|
392
|
+
hiringOrganization: Organization
|
|
393
|
+
datePosted: str
|
|
394
|
+
validThrough: str
|
|
395
|
+
jobLocation: Optional[Place]
|
|
396
|
+
baseSalary: Optional[MonetaryAmount]
|
|
397
|
+
qualifications: str
|
|
398
|
+
skills: List[str]
|
|
399
|
+
responsibilities: str
|
|
400
|
+
educationRequirements: str
|
|
401
|
+
experienceRequirements: str
|
|
402
|
+
|
|
403
|
+
class Meta:
|
|
404
|
+
schema_type = "JobPosting"
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
@register_renderer("ImageObject")
|
|
408
|
+
def render_imageobject(model: "BaseModel", top_level: bool) -> str:
|
|
409
|
+
"""
|
|
410
|
+
Render an ImageObject in a custom way:
|
|
411
|
+
<div typeof="ImageObject">
|
|
412
|
+
<h2 property="name">...</h2>
|
|
413
|
+
<img src="..." alt="..." property="contentUrl"/>
|
|
414
|
+
...
|
|
415
|
+
</div>
|
|
416
|
+
"""
|
|
417
|
+
# You can read fields from the model (like name, url, caption, etc.).
|
|
418
|
+
# If you used 'url' as the field that is the image's src, we can do:
|
|
419
|
+
name = getattr(model, "name", None) # or None if not present
|
|
420
|
+
url = getattr(model, "url", None)
|
|
421
|
+
caption = getattr(model, "caption", None)
|
|
422
|
+
width = getattr(model, "width", None)
|
|
423
|
+
height = getattr(model, "height", None)
|
|
424
|
+
|
|
425
|
+
schema_type = getattr(model.Meta, 'schema_type', model.__class__.__name__)
|
|
426
|
+
|
|
427
|
+
container_open = ""
|
|
428
|
+
if top_level:
|
|
429
|
+
container_open = f'<div vocab="https://schema.org/" typeof="{escape(schema_type)}">' # noqa
|
|
430
|
+
else:
|
|
431
|
+
# when nested, we might do property="image" or property=schema_type
|
|
432
|
+
container_open = f'<div property="{escape(schema_type)}" typeof="{escape(schema_type)}">' # noqa
|
|
433
|
+
|
|
434
|
+
pieces = [container_open]
|
|
435
|
+
|
|
436
|
+
# Render name as <h2 property="name">Name</h2> if present
|
|
437
|
+
if name:
|
|
438
|
+
name_esc = escape(str(name))
|
|
439
|
+
pieces.append(f'<h2 property="name">{name_esc}</h2>')
|
|
440
|
+
|
|
441
|
+
# Now create an <img> that has property="contentUrl" or "url"
|
|
442
|
+
if url:
|
|
443
|
+
url_esc = escape(str(url))
|
|
444
|
+
caption_esc = escape(str(caption or ""))
|
|
445
|
+
# alt can come from caption
|
|
446
|
+
snippet = f'<img property="contentUrl" src="{url_esc}" alt="{caption_esc}"'
|
|
447
|
+
if width:
|
|
448
|
+
snippet += f' width="{escape(str(width))}"'
|
|
449
|
+
if height:
|
|
450
|
+
snippet += f' height="{escape(str(height))}"'
|
|
451
|
+
snippet += ' />'
|
|
452
|
+
pieces.append(snippet)
|
|
453
|
+
|
|
454
|
+
# If we have other fields not individually handled, we could do a fallback
|
|
455
|
+
# to the normal field iteration. But for brevity, let's omit that here.
|
|
456
|
+
# E.g. leftover = model.render_remaining_fields(…)
|
|
457
|
+
# pieces.append(leftover)
|
|
458
|
+
|
|
459
|
+
pieces.append('</div>')
|
|
460
|
+
return "\n".join(pieces)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
@register_renderer("GeoCoordinates")
|
|
464
|
+
def render_geocoordinates(model: "BaseModel", top_level: bool) -> str:
|
|
465
|
+
"""
|
|
466
|
+
Custom renderer for GeoCoordinates:
|
|
467
|
+
<div property="geo" typeof="GeoCoordinates">
|
|
468
|
+
<meta property="latitude" content="40.75"/>
|
|
469
|
+
<meta property="longitude" content="-73.98"/>
|
|
470
|
+
...
|
|
471
|
+
</div>
|
|
472
|
+
"""
|
|
473
|
+
lat = getattr(model, "latitude", None)
|
|
474
|
+
lng = getattr(model, "longitude", None)
|
|
475
|
+
elevation = getattr(model, "elevation", None)
|
|
476
|
+
|
|
477
|
+
schema_type = getattr(model.Meta, 'schema_type', model.__class__.__name__)
|
|
478
|
+
if top_level:
|
|
479
|
+
container_open = f'<div vocab="https://schema.org/" typeof="{escape(schema_type)}">' # noqa
|
|
480
|
+
else:
|
|
481
|
+
container_open = f'<div property="{escape(schema_type)}" typeof="{escape(schema_type)}">' # noqa
|
|
482
|
+
|
|
483
|
+
pieces = [container_open]
|
|
484
|
+
|
|
485
|
+
# For numeric fields, we might prefer <meta ... content="..."/>
|
|
486
|
+
if lat is not None:
|
|
487
|
+
pieces.append(
|
|
488
|
+
f'<meta property="latitude" content="{escape(str(lat))}" />'
|
|
489
|
+
)
|
|
490
|
+
if lng is not None:
|
|
491
|
+
pieces.append(
|
|
492
|
+
f'<meta property="longitude" content="{escape(str(lng))}" />'
|
|
493
|
+
)
|
|
494
|
+
if elevation is not None:
|
|
495
|
+
pieces.append(
|
|
496
|
+
f'<meta property="elevation" content="{escape(str(elevation))}" />'
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
pieces.append('</div>')
|
|
500
|
+
return "\n".join(pieces)
|
|
File without changes
|