thanhnt-cf commited on
Commit
4e55bb0
·
1 Parent(s): a9d8d74

add code to convert cf-style schema to pydantic

Browse files
Files changed (2) hide show
  1. app.py +160 -22
  2. app/services/base.py +64 -116
app.py CHANGED
@@ -31,13 +31,15 @@ async def forward_request(
31
 
32
  try:
33
  # convert attributes to schema
34
- attributes = import_for_schema + attributes
35
  try:
36
- exec(attributes, globals())
37
  except:
38
  raise gr.Error(
39
  "Invalid `Attribute Schema`. Please insert valid schema following the example."
40
  )
 
 
41
 
42
  if product_data == "":
43
  product_data = "{}"
@@ -87,14 +89,15 @@ async def forward_request(
87
 
88
  try:
89
  json_attributes, reevaluated = await service.extract_attributes_with_validation(
90
- Product, # type: ignore
91
  ai_model,
92
  None,
93
  product_taxonomy,
94
  product_data_object, # type: ignore
95
  img_paths=img_paths,
96
  )
97
- except:
 
98
  raise gr.Error("Failed to extract attributes. Something went wrong.")
99
  finally:
100
  # remove temp folder anyway
@@ -141,12 +144,7 @@ class Style(BaseModel):
141
  smock: int = Field(..., description="Smock: Loose-fitting with gathered or shirred sections, usually on bodice or neckline.")
142
  corset: int = Field(..., description="Corset: Structured bodice with boning or lacing that shapes the waist.")
143
  jumper_dress: int = Field(..., description="Jumper Dress: Layered dress style similar to a pinafore, often more casual or thick-strapped.")
144
- blazer_dress: int = Field(..., description="Blazer Dress: Tailored like a blazer or suit jacket, often double-breasted or lapelled.")
145
- asymmetric: int = Field(..., description="Asymmetric: Dress with a non-symmetrical hem, neckline, or sleeve design.")
146
  shift: int = Field(..., description="Shift: Simple, straight dress with no defined waist, typically above the knee.")
147
- drop_waist: int = Field(..., description="Drop waist: Waistline sits low on the hips, usually with a loose top and flared skirt.")
148
- empire: int = Field(..., description="Empire: High waistline just below the bust, flowing skirt from there downward.")
149
- modest: int = Field(..., description="Modest: Covers most of the body, with high neckline, long sleeves, and longer hemline.")
150
 
151
 
152
  class SleeveLength(BaseModel):
@@ -161,14 +159,11 @@ class Neckline(BaseModel):
161
  v_neck: int = Field(..., description="V Neck: Neckline dips down in the shape of a 'V', varying from shallow to deep.")
162
  sweetheart: int = Field(..., description="Sweetheart: A heart-shaped neckline, often curving over the bust and dipping in the center.")
163
  round_neck: int = Field(..., description="Round Neck: Circular neckline sitting around the base of the neck.")
164
- halter_neck: int = Field(..., description="Halter Neck: Straps go around the neck, leaving shoulders and upper back exposed.")
165
  square_neck: int = Field(..., description="Square Neck: Straight horizontal cut across the chest with vertical sides, forming a square.")
166
  high_neck: int = Field(..., description="High Neck: Extends up the neck slightly but not folded like a turtle neck.")
167
  crew_neck: int = Field(..., description="Crew Neck: High, rounded neckline that sits close to the neck.")
168
  turtle_neck: int = Field(..., description="Turtle Neck: High neckline that folds over and covers the neck completely.")
169
  off_the_shoulder: int = Field(..., description="Off the Shoulder: Sits below the shoulders, exposing the shoulders and collarbone.")
170
- one_shoulder: int = Field(..., description="One Shoulder: Covers one shoulder only, leaving the other bare.")
171
- boat_neck: int = Field(..., description="Boat Neck: Wide, shallow neckline that runs almost horizontally from shoulder to shoulder.")
172
 
173
 
174
  class Pattern(BaseModel):
@@ -178,7 +173,6 @@ class Pattern(BaseModel):
178
  plain: int = Field(..., description="Plain")
179
  geometric: int = Field(..., description="Geometric pattern")
180
  logo: int = Field(..., description="Logo print")
181
- graphic_print: int = Field(..., description="Graphic print")
182
  other: int = Field(..., description="Other pattern")
183
 
184
 
@@ -188,13 +182,8 @@ class Fabric(BaseModel):
188
  linen: int = Field(..., description="Linen")
189
  satin: int = Field(..., description="Satin")
190
  silk: int = Field(..., description="Silk")
191
- sequin: int = Field(..., description="Sequin")
192
  leather: int = Field(..., description="Leather")
193
  velvet: int = Field(..., description="Velvet")
194
- knit: int = Field(..., description="Knit")
195
- lace: int = Field(..., description="Lace")
196
- suede: int = Field(..., description="Suede")
197
- sheer: int = Field(..., description="Sheer")
198
  polyester: int = Field(..., description="Polyester")
199
  viscose: int = Field(..., description="Viscose")
200
 
@@ -231,9 +220,6 @@ class Occasion(BaseModel):
231
  mother_of_the_bride: int = Field(..., description="Mother of the bride dress")
232
  party: int = Field(..., description="Party wear")
233
  prom: int = Field(..., description="Prom dress")
234
- wedding_guest: int = Field(..., description="Wedding guest dress")
235
- work: int = Field(..., description="Work attire")
236
- sportswear: int = Field(..., description="Sportswear")
237
 
238
 
239
  class Season(BaseModel):
@@ -256,6 +242,158 @@ class Product(BaseModel):
256
  occasion: Occasion = Field(..., description="Can have multiple values ,Occasion of the dress")
257
  season: Season = Field(..., description="Single value ,Season of the dress")
258
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  description = """
260
  This is a simple demo for Attribution. Follow the steps below:
261
 
@@ -339,7 +477,7 @@ with gr.Blocks(title="Internal Demo for Attribution") as demo:
339
  with gr.Column():
340
  attributes = gr.TextArea(
341
  label="Attribute Schema",
342
- value=sample_schema,
343
  placeholder="Enter schema here or use Add Attributes below",
344
  interactive=True,
345
  lines=30,
 
31
 
32
  try:
33
  # convert attributes to schema
34
+ attributes = "attributes_object = {" + attributes + "}"
35
  try:
36
+ attributes = exec(attributes, globals())
37
  except:
38
  raise gr.Error(
39
  "Invalid `Attribute Schema`. Please insert valid schema following the example."
40
  )
41
+ for key, value in attributes_object.items(): # type: ignore
42
+ attributes_object[key] = Attribute(**value) # type: ignore
43
 
44
  if product_data == "":
45
  product_data = "{}"
 
89
 
90
  try:
91
  json_attributes, reevaluated = await service.extract_attributes_with_validation(
92
+ attributes_object, # type: ignore
93
  ai_model,
94
  None,
95
  product_taxonomy,
96
  product_data_object, # type: ignore
97
  img_paths=img_paths,
98
  )
99
+ except Exception as e:
100
+ print(e)
101
  raise gr.Error("Failed to extract attributes. Something went wrong.")
102
  finally:
103
  # remove temp folder anyway
 
144
  smock: int = Field(..., description="Smock: Loose-fitting with gathered or shirred sections, usually on bodice or neckline.")
145
  corset: int = Field(..., description="Corset: Structured bodice with boning or lacing that shapes the waist.")
146
  jumper_dress: int = Field(..., description="Jumper Dress: Layered dress style similar to a pinafore, often more casual or thick-strapped.")
 
 
147
  shift: int = Field(..., description="Shift: Simple, straight dress with no defined waist, typically above the knee.")
 
 
 
148
 
149
 
150
  class SleeveLength(BaseModel):
 
159
  v_neck: int = Field(..., description="V Neck: Neckline dips down in the shape of a 'V', varying from shallow to deep.")
160
  sweetheart: int = Field(..., description="Sweetheart: A heart-shaped neckline, often curving over the bust and dipping in the center.")
161
  round_neck: int = Field(..., description="Round Neck: Circular neckline sitting around the base of the neck.")
 
162
  square_neck: int = Field(..., description="Square Neck: Straight horizontal cut across the chest with vertical sides, forming a square.")
163
  high_neck: int = Field(..., description="High Neck: Extends up the neck slightly but not folded like a turtle neck.")
164
  crew_neck: int = Field(..., description="Crew Neck: High, rounded neckline that sits close to the neck.")
165
  turtle_neck: int = Field(..., description="Turtle Neck: High neckline that folds over and covers the neck completely.")
166
  off_the_shoulder: int = Field(..., description="Off the Shoulder: Sits below the shoulders, exposing the shoulders and collarbone.")
 
 
167
 
168
 
169
  class Pattern(BaseModel):
 
173
  plain: int = Field(..., description="Plain")
174
  geometric: int = Field(..., description="Geometric pattern")
175
  logo: int = Field(..., description="Logo print")
 
176
  other: int = Field(..., description="Other pattern")
177
 
178
 
 
182
  linen: int = Field(..., description="Linen")
183
  satin: int = Field(..., description="Satin")
184
  silk: int = Field(..., description="Silk")
 
185
  leather: int = Field(..., description="Leather")
186
  velvet: int = Field(..., description="Velvet")
 
 
 
 
187
  polyester: int = Field(..., description="Polyester")
188
  viscose: int = Field(..., description="Viscose")
189
 
 
220
  mother_of_the_bride: int = Field(..., description="Mother of the bride dress")
221
  party: int = Field(..., description="Party wear")
222
  prom: int = Field(..., description="Prom dress")
 
 
 
223
 
224
 
225
  class Season(BaseModel):
 
242
  occasion: Occasion = Field(..., description="Can have multiple values ,Occasion of the dress")
243
  season: Season = Field(..., description="Single value ,Season of the dress")
244
  """
245
+
246
+ cf_style_schema = """
247
+ "Length": {
248
+ "description": "Length of dress",
249
+ "data_type": "string",
250
+ "allowed_values": [
251
+ "Maxi",
252
+ "Knee Length",
253
+ "Mini",
254
+ "Midi"
255
+ ]
256
+ },
257
+ "Style": {
258
+ "description": "Select the most appropriate dress style based on the garment's silhouette, fit, structural features, and overall design. Focus on how the dress is constructed and worn: whether it is fitted or loose, whether it has defining elements such as shirring, boning, buttons, collars, tiers, layering, or wrap ties. Ignore color, pattern, or fabric unless they directly influence the structure (e.g., stretch fabric for Bodycon). Use the visual cues of the neckline, sleeves, waistline, hemline, and closure type to guide your choice. Only select one style that best captures the dominant structural or design identity of the dress. Refer to the following definitions when uncertain: - 'A Line': Fitted at the top and gradually flares toward the hem, forming an 'A' shape.- 'Bodycon': Tight-fitting and figure-hugging, usually made with stretchy fabric.- 'Column': Straight silhouette from top to bottom, with minimal shaping or flare.- 'Shirt Dress': Structured like a shirt with buttons, collar, and sleeves; may include a belt.- 'Wrap Dress': Features a front closure that wraps and ties at the side or back.- 'Slip': Lightweight, spaghetti-strap dress with minimal structure, often bias-cut.- 'Kaftan': Very loose, flowing garment with wide sleeves and minimal shaping.- 'Smock': Loose-fitting with gathered or shirred sections (usually bodice or neckline).- 'Corset': Structured bodice with boning or lacing that shapes the waist.- 'Pinafore': Sleeveless over-dress, often worn layered over another top.- 'Jumper Dress': Layered dress style similar to a pinafore, often more casual or thick-strapped.- 'Blazer Dress': Tailored like a blazer or suit jacket, often double-breasted or lapelled.- 'Tunic': Loose and straight-cut, often worn short or over pants/leggings.- 'Gown': Full-length, formal dress with a structured or dramatic silhouette.- 'Asymmetric': Dress with a non-symmetrical hem, neckline, or sleeve design.- 'Shift': Simple, straight dress with no defined waist, typically above the knee.- 'Drop waist': Waistline sits low on the hips, usually with a loose top and flared skirt.- 'Empire': High waistline just below the bust, flowing skirt from there downward.- 'Modest': Covers most of the body, with high neckline, long sleeves, and longer hemline. Use structural cues over stylistic interpretation. Do not infer intent (e.g., party, formal) unless it’s directly tied to the construction.",
259
+ "data_type": "string",
260
+ "allowed_values": [
261
+ "A Line",
262
+ "Bodycon",
263
+ "Column",
264
+ "Shirt Dress",
265
+ "Wrap Dress",
266
+ "Smock",
267
+ "Corset",
268
+ "Tunic",
269
+ "Asymmetric",
270
+ "Shift",
271
+ "Modest"
272
+ ]
273
+ },
274
+ "Sleeve_length": {
275
+ "description": "Length of sleeves on dress",
276
+ "data_type": "string",
277
+ "allowed_values": [
278
+ "Sleeveless",
279
+ "Three quarters Sleeve",
280
+ "Long Sleeve",
281
+ "Short Sleeve",
282
+ "Strapless"
283
+ ]
284
+ },
285
+ "Neckline": {
286
+ "description": "Identify the neckline style based on the visible shape and structure of the neckline area. Focus on the cut and contour around the collarbone, shoulders, and upper chest. Only choose the neckline that best represents the dominant design — ignore collars, patterns, or styling details unless they significantly alter the neckline shape. Use the following definitions for clarity: - 'V Neck': Neckline dips down in the shape of a 'V', varying from shallow to deep. - 'Sweetheart': A heart-shaped neckline, often curving over the bust and dipping in the center. - 'Round Neck': Circular neckline sitting around the base of the neck, not as high as a crew neck. - 'Halter Neck': Straps go around the neck, leaving shoulders and upper back exposed. - 'Square Neck': Straight horizontal cut across the chest with vertical sides, forming a square. - 'High Neck': Extends up the neck slightly but not folded like a turtle neck. - 'Crew Neck': High, rounded neckline that sits close to the neck (commonly found in T-shirts). - 'Cowl Neck': Draped or folded neckline that hangs in soft folds. - 'Turtle Neck': High neckline that folds over and covers the neck completely. - 'Off the Shoulder': Sits below the shoulders, exposing the shoulders and collarbone. - 'One Shoulder': Covers one shoulder only, leaving the other bare. - 'Bandeau': Straight, strapless neckline that wraps across the bust. - 'Boat Neck': Wide, shallow neckline that runs almost horizontally from shoulder to shoulder. - 'Scoop Neck': U-shaped neckline, typically deeper than a round neck. - Always prioritize structure over styling — for example, a dress with embellishment or a mesh overlay still counts as 'V Neck' if the main shape is a V. If a neckline is borderline between two types, choose the simpler or more dominant structure.",
287
+ "data_type": "string",
288
+ "allowed_values": [
289
+ "V Neck",
290
+ "Sweetheart",
291
+ "Round Neck",
292
+ "Halter Neck",
293
+ "Square Neck",
294
+ "Cowl Neck",
295
+ "Turtle Neck",
296
+ "Off the shoulder",
297
+ "Boat Neck",
298
+ "Scoop Neck"
299
+ ]
300
+ },
301
+ "pattern": {
302
+ "description": "Pattern of the garment",
303
+ "data_type": "string",
304
+ "allowed_values": [
305
+ "Floral",
306
+ "Stripe",
307
+ "Leopard Print",
308
+ "Spot",
309
+ "Plain",
310
+ "Geometric",
311
+ "Logo",
312
+ "Graphic print",
313
+ "Check",
314
+ "other"
315
+ ]
316
+ },
317
+ "fabric": {
318
+ "description": "Material of the garment",
319
+ "data_type": "string",
320
+ "allowed_values": [
321
+ "Cotton",
322
+ "Denim",
323
+ "Jersey",
324
+ "Linen",
325
+ "Satin",
326
+ "Silk",
327
+ "Leather",
328
+ "Velvet",
329
+ "Corduroy",
330
+ "Ponte",
331
+ "Knit",
332
+ "Lace",
333
+ "Polyester",
334
+ "Viscose"
335
+ ]
336
+ },
337
+ "features": {
338
+ "description": "special features of the garment",
339
+ "data_type": "list[string]",
340
+ "allowed_values": [
341
+ "Pockets",
342
+ "Lined",
343
+ "Cut Out",
344
+ "Backless",
345
+ "none"
346
+ ]
347
+ },
348
+ "Closure": {
349
+ "description": "Closure of the garment. How it is closed",
350
+ "data_type": "list[string]",
351
+ "allowed_values": [
352
+ "Button",
353
+ "Zip",
354
+ "Press Stud",
355
+ "Clasp"
356
+ ]
357
+ },
358
+ "Body_Fit": {
359
+ "description": "How the dress fits the body",
360
+ "data_type": "string",
361
+ "allowed_values": [
362
+ "Petite",
363
+ "Maternity",
364
+ "Regular",
365
+ "Tall",
366
+ "Plus Size"
367
+ ]
368
+ },
369
+ "Occasion": {
370
+ "description": "What occasions do the dress match",
371
+ "data_type": "list[string]",
372
+ "allowed_values": [
373
+ "Beach",
374
+ "Casual",
375
+ "Cocktail",
376
+ "Day",
377
+ "Bridal",
378
+ "Bridesmaid",
379
+ "Evening",
380
+ "Mother of the Bride",
381
+ "Party",
382
+ "Prom"
383
+ ]
384
+ },
385
+ "Season": {
386
+ "description": "What season do the dress match",
387
+ "data_type": "list[string]",
388
+ "allowed_values": [
389
+ "Spring",
390
+ "Summer",
391
+ "Autumn",
392
+ "Winter"
393
+ ]
394
+ }
395
+ """[1:]
396
+
397
  description = """
398
  This is a simple demo for Attribution. Follow the steps below:
399
 
 
477
  with gr.Column():
478
  attributes = gr.TextArea(
479
  label="Attribute Schema",
480
+ value=cf_style_schema,
481
  placeholder="Enter schema here or use Add Attributes below",
482
  interactive=True,
483
  lines=30,
app/services/base.py CHANGED
@@ -11,115 +11,47 @@ from app.schemas.schema_tools import (
11
  )
12
 
13
 
14
- example_data = example_data = {
15
- "length": {
16
- "maxi": 100,
17
- "knee_length": 0,
18
- "mini": 0,
19
- "midi": 0
20
- },
21
- "style": {
22
- "a_line": 0,
23
- "bodycon": 0,
24
- "shirt_dress": 0,
25
- "wrap_dress": 0,
26
- "slip": 0,
27
- "smock": 0,
28
- "corset": 100,
29
- "jumper_dress": 0,
30
- "blazer_dress": 0,
31
- "asymmetric": 0,
32
- "shift": 0,
33
- "drop_waist": 0,
34
- "empire": 0,
35
- "modest": 0
36
- },
37
- "sleeve_length": {
38
- "sleeveless": 0,
39
- "three_quarters_sleeve": 0,
40
- "long_sleeve": 0,
41
- "short_sleeve": 0,
42
- "strapless": 100
43
- },
44
- "neckline": {
45
- "v_neck": 0,
46
- "sweetheart": 100,
47
- "round_neck": 0,
48
- "halter_neck": 0,
49
- "square_neck": 0,
50
- "high_neck": 0,
51
- "crew_neck": 0,
52
- "turtle_neck": 0,
53
- "off_the_shoulder": 0,
54
- "one_shoulder": 0,
55
- "boat_neck": 0
56
- },
57
- "pattern": {
58
- "floral": 0,
59
- "stripe": 0,
60
- "leopard_print": 0,
61
- "plain": 100,
62
- "geometric": 0,
63
- "logo": 0,
64
- "graphic_print": 0,
65
- "other": 0
66
- },
67
- "fabric": {
68
- "cotton": 0,
69
- "denim": 0,
70
- "linen": 0,
71
- "satin": 0,
72
- "silk": 0,
73
- "sequin": 0,
74
- "leather": 0,
75
- "velvet": 100,
76
- "knit": 0,
77
- "lace": 0,
78
- "suede": 0,
79
- "sheer": 0,
80
- "polyester": 0,
81
- "viscose": 0
82
- },
83
- "features": {
84
- "pockets": 0,
85
- "lined": 0,
86
- "cut_out": 0,
87
- "backless": 0,
88
- "none": 100
89
- },
90
- "closure": {
91
- "button": 0,
92
- "zip": 0,
93
- "press_stud": 0,
94
- "clasp": 0
95
- },
96
- "body_fit": {
97
- "petite": 0,
98
- "maternity": 0,
99
- "regular": 100,
100
- "tall": 0,
101
- "plus_size": 0
102
- },
103
- "occasion": {
104
- "beach": 0,
105
- "casual": 0,
106
- "cocktail": 0,
107
- "day": 0,
108
- "evening": 100,
109
- "mother_of_the_bride": 0,
110
- "party": 0,
111
- "prom": 0,
112
- "wedding_guest": 0,
113
- "work": 0,
114
- "sportswear": 0
115
- },
116
- "season": {
117
- "spring": 0,
118
- "summer": 0,
119
- "autumn": 0,
120
- "winter": 100
121
- }
122
- }
123
 
124
  class BaseAttributionService(ABC):
125
  @abstractmethod
@@ -152,7 +84,7 @@ class BaseAttributionService(ABC):
152
 
153
  async def extract_attributes_with_validation(
154
  self,
155
- attributes_model: Type[BaseModel],
156
  ai_model: str,
157
  img_urls: List[str],
158
  product_taxonomy: str,
@@ -163,9 +95,21 @@ class BaseAttributionService(ABC):
163
  # validate_json_schema(schema)
164
 
165
  # create mappings for keys of attributes, to make the key following naming convention of python variables
166
- schema = attributes_model.model_json_schema()
 
 
 
 
 
 
 
 
 
 
 
 
167
  data = await self.extract_attributes(
168
- attributes_model,
169
  ai_model,
170
  img_urls,
171
  product_taxonomy if product_taxonomy != "" else "main",
@@ -173,12 +117,11 @@ class BaseAttributionService(ABC):
173
  # pil_images=pil_images, # temporarily removed to save cost
174
  img_paths=img_paths,
175
  )
176
- # data = example_data
177
  validate_json_data(data, schema)
178
 
179
  str_data = str(data)
180
  reevaluate_data = await self.reevaluate_atributes(
181
- attributes_model,
182
  ai_model,
183
  img_urls,
184
  product_taxonomy if product_taxonomy != "" else "main",
@@ -188,7 +131,7 @@ class BaseAttributionService(ABC):
188
  )
189
 
190
  init_reevaluate_data = {}
191
- for field_name, field in attributes_model.model_fields.items(): # type: ignore
192
  print(f"{field_name}: {field.description}")
193
  if "single value" in field.description.lower():
194
  max_percentage = 0
@@ -203,7 +146,12 @@ class BaseAttributionService(ABC):
203
  init_list.append(k)
204
  init_reevaluate_data[field_name] = init_list
205
 
206
- return data, init_reevaluate_data
 
 
 
 
 
207
 
208
  async def follow_schema_with_validation(
209
  self, schema: Dict[str, Any], data: Dict[str, Any]
 
11
  )
12
 
13
 
14
+ def cf_style_to_pydantic_percentage_shema(
15
+ cf_style_schema: dict,
16
+ ) -> str:
17
+ """
18
+ Convert CF style schema to Pydantic schema
19
+ """
20
+ print(f'{cf_style_schema}')
21
+ attributes_line_in_product = []
22
+ values_classes = []
23
+ for attribute, attribute_info in cf_style_schema.items():
24
+ multiple = False
25
+ if "list" in attribute_info.data_type:
26
+ multiple = True
27
+ else:
28
+ multiple = False
29
+ class_name = "Class_" + attribute.capitalize()
30
+ multiple_desc = "Can have multiple values" if multiple else "Single value only"
31
+ attribute_desc = attribute_info.description
32
+ attribute_line = f'{attribute}: {class_name} = Field("", description="{multiple_desc}, {attribute_desc}")'
33
+
34
+ class_code = f"""
35
+ class {class_name}(BaseModel):
36
+
37
+ """
38
+ for value in attribute_info.allowed_values:
39
+ class_code += f" {value.lower().replace(' ', '_').replace('-', '_')}: int\n"
40
+
41
+ values_classes.append(class_code)
42
+ attributes_line_in_product.append(attribute_line)
43
+ attributes_line = "\n ".join(attributes_line_in_product)
44
+ values_classes_code = "\n".join(values_classes)
45
+ pydantic_schema = f"""
46
+ from pydantic import BaseModel, Field
47
+ {values_classes_code}
48
+ class Product(BaseModel):
49
+ {attributes_line}
50
+ """
51
+ pydantic_code = pydantic_schema.strip()
52
+ exec(pydantic_code, globals())
53
+ return Product
54
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  class BaseAttributionService(ABC):
57
  @abstractmethod
 
84
 
85
  async def extract_attributes_with_validation(
86
  self,
87
+ attributes: Dict[str, Any],
88
  ai_model: str,
89
  img_urls: List[str],
90
  product_taxonomy: str,
 
95
  # validate_json_schema(schema)
96
 
97
  # create mappings for keys of attributes, to make the key following naming convention of python variables
98
+ forward_mapping = {}
99
+ reverse_mapping = {}
100
+ for i, key in enumerate(attributes.keys()):
101
+ forward_mapping[key] = f'{to_snake_case(key)}_{i}'
102
+ reverse_mapping[f'{to_snake_case(key)}_{i}'] = key
103
+
104
+ transformed_attributes = {}
105
+ for key, value in attributes.items():
106
+ transformed_attributes[forward_mapping[key]] = value
107
+
108
+ # attributes_model = convert_attribute_to_model(transformed_attributes)
109
+ attributes_percentage_model = cf_style_to_pydantic_percentage_shema(transformed_attributes)
110
+ schema = attributes_percentage_model.model_json_schema()
111
  data = await self.extract_attributes(
112
+ attributes_percentage_model,
113
  ai_model,
114
  img_urls,
115
  product_taxonomy if product_taxonomy != "" else "main",
 
117
  # pil_images=pil_images, # temporarily removed to save cost
118
  img_paths=img_paths,
119
  )
 
120
  validate_json_data(data, schema)
121
 
122
  str_data = str(data)
123
  reevaluate_data = await self.reevaluate_atributes(
124
+ attributes_percentage_model,
125
  ai_model,
126
  img_urls,
127
  product_taxonomy if product_taxonomy != "" else "main",
 
131
  )
132
 
133
  init_reevaluate_data = {}
134
+ for field_name, field in attributes_percentage_model.model_fields.items(): # type: ignore
135
  print(f"{field_name}: {field.description}")
136
  if "single value" in field.description.lower():
137
  max_percentage = 0
 
146
  init_list.append(k)
147
  init_reevaluate_data[field_name] = init_list
148
 
149
+ # reverse the key mapping to the original keys
150
+ reverse_data = {}
151
+ for key, value in init_reevaluate_data.items():
152
+ reverse_data[reverse_mapping[key]] = value
153
+
154
+ return data, reverse_data
155
 
156
  async def follow_schema_with_validation(
157
  self, schema: Dict[str, Any], data: Dict[str, Any]