@ -113,6 +113,130 @@ class CourseCategory(models.Model):
def course_count ( self ) :
def course_count ( self ) :
return self . courses . exclude ( status = " inactive " ) . count ( )
return self . courses . exclude ( status = " inactive " ) . count ( )
LEVEL_TRANSLATIONS = {
' beginner ' : {
' en ' : ' Beginner ' ,
' ru ' : ' Начальный ' ,
} ,
' mid ' : {
' en ' : ' Mid Level ' ,
' ru ' : ' Средний ' ,
} ,
' advanced ' : {
' en ' : ' Advanced ' ,
' ru ' : ' Продвинутый ' ,
}
}
STATUS_TRANSLATIONS = {
' inactive ' : {
' en ' : ' Inactive ' ,
' ru ' : ' Неактивен ' ,
} ,
' upcoming ' : {
' en ' : ' Upcoming ' ,
' ru ' : ' Предстоящий ' ,
} ,
' registering ' : {
' en ' : ' Registering ' ,
' ru ' : ' Регистрация ' ,
} ,
' ongoing ' : {
' en ' : ' Ongoing ' ,
' ru ' : ' В процессе ' ,
} ,
' finished ' : {
' en ' : ' Finished ' ,
' ru ' : ' Завершен ' ,
}
}
def normalize_level ( value ) :
if not value :
return ' beginner '
text = extract_text_from_json ( value ) . lower ( ) . strip ( )
if ' beginner ' in text or ' начальн ' in text or ' начинающ ' in text :
return ' beginner '
if ' mid ' in text or ' средн ' in text :
return ' mid '
if ' advanced ' in text or ' продвинут ' in text :
return ' advanced '
return ' beginner '
def normalize_status ( value ) :
if not value :
return ' inactive '
text = extract_text_from_json ( value ) . lower ( ) . strip ( )
if ' inactive ' in text or ' неактив ' in text :
return ' inactive '
if ' upcoming ' in text or ' предстоящ ' in text :
return ' upcoming '
if ' registering ' in text or ' регистрац ' in text :
return ' registering '
if ' ongoing ' in text or ' в процессе ' in text :
return ' ongoing '
if ' finished ' in text or ' заверш ' in text or ' законч ' in text :
return ' finished '
return ' inactive '
WEEKDAY_TRANSLATIONS = {
' monday ' : {
' en ' : ' Monday ' ,
' ru ' : ' Понедельник ' ,
' fa ' : ' دوشنبه ' ,
} ,
' tuesday ' : {
' en ' : ' Tuesday ' ,
' ru ' : ' Вторник ' ,
' fa ' : ' سهشنبه ' ,
} ,
' wednesday ' : {
' en ' : ' Wednesday ' ,
' ru ' : ' Среда ' ,
' fa ' : ' چهارشنبه ' ,
} ,
' thursday ' : {
' en ' : ' Thursday ' ,
' ru ' : ' Четверг ' ,
' fa ' : ' پنجشنبه ' ,
} ,
' friday ' : {
' en ' : ' Friday ' ,
' ru ' : ' Пятница ' ,
' fa ' : ' جمعه ' ,
} ,
' saturday ' : {
' en ' : ' Saturday ' ,
' ru ' : ' Суббота ' ,
' fa ' : ' شنبه ' ,
} ,
' sunday ' : {
' en ' : ' Sunday ' ,
' ru ' : ' Воскресенье ' ,
' fa ' : ' یکشنبه ' ,
}
}
def normalize_weekday_to_key ( day_value ) :
if not day_value :
return ' saturday '
txt = str ( day_value ) . lower ( ) . strip ( )
if ' monday ' in txt or ' понедельник ' in txt or ' دوشنبه ' in txt :
return ' monday '
if ' tuesday ' in txt or ' вторник ' in txt or ' سه شنبه ' in txt or ' سهشنبه ' in txt :
return ' tuesday '
if ' wednesday ' in txt or ' среда ' in txt or ' چهارشنبه ' in txt :
return ' wednesday '
if ' thursday ' in txt or ' четверг ' in txt or ' پنج شنبه ' in txt or ' پنجشنبه ' in txt :
return ' thursday '
if ' friday ' in txt or ' пятница ' in txt or ' جمعه ' in txt :
return ' friday '
if ' saturday ' in txt or ' суббота ' in txt or ' شنبه ' in txt :
return ' saturday '
if ' sunday ' in txt or ' воскресенье ' in txt or ' یک شنبه ' in txt or ' یکشنبه ' in txt :
return ' sunday '
return ' saturday '
class Course ( models . Model ) :
class Course ( models . Model ) :
class LevelChoices ( TextChoices ) :
class LevelChoices ( TextChoices ) :
@ -163,8 +287,8 @@ class Course(models.Model):
duration = models . PositiveIntegerField ( verbose_name = _ ( ' Duration (in hours) ' ) )
duration = models . PositiveIntegerField ( verbose_name = _ ( ' Duration (in hours) ' ) )
lessons_count = models . PositiveIntegerField ( verbose_name = _ ( ' Number of Lessons ' ) )
lessons_count = models . PositiveIntegerField ( verbose_name = _ ( ' Number of Lessons ' ) )
description = models . TextField ( verbose_name = _ ( ' Course Description ' ) )
short_description = models . CharField ( max_length = 500 , blank = True , null = True , verbose_name = _ ( " Short Description " ) )
description = models . JSONField ( default = list , null = True , blank = True , verbose_name = _ ( ' Course Description ' ) )
short_description = models . JSONField ( default = list , null = True , blank = True , verbose_name = _ ( " Short Description " ) )
status = models . JSONField ( default = list , null = True , blank = True , verbose_name = _ ( ' Course Status ' ) )
status = models . JSONField ( default = list , null = True , blank = True , verbose_name = _ ( ' Course Status ' ) )
is_free = models . BooleanField ( default = True , verbose_name = _ ( ' Is Free ' ) )
is_free = models . BooleanField ( default = True , verbose_name = _ ( ' Is Free ' ) )
price = models . DecimalField ( max_digits = 10 , decimal_places = 2 , default = 0.00 , verbose_name = _ ( ' Course Price ' ) )
price = models . DecimalField ( max_digits = 10 , decimal_places = 2 , default = 0.00 , verbose_name = _ ( ' Course Price ' ) )
@ -225,9 +349,79 @@ class Course(models.Model):
self . slug = [ ]
self . slug = [ ]
else :
else :
self . slug = format_multilingual_field ( self . slug )
self . slug = format_multilingual_field ( self . slug )
self . level = format_multilingual_field ( self . level )
self . status = format_multilingual_field ( self . status )
self . timing = format_multilingual_field ( self . timing )
level_key = normalize_level ( self . level )
self . level = [
{ " title " : LEVEL_TRANSLATIONS [ level_key ] [ " en " ] , " language_code " : " en " } ,
{ " title " : LEVEL_TRANSLATIONS [ level_key ] [ " ru " ] , " language_code " : " ru " } ,
]
status_key = normalize_status ( self . status )
self . status = [
{ " title " : STATUS_TRANSLATIONS [ status_key ] [ " en " ] , " language_code " : " en " } ,
{ " title " : STATUS_TRANSLATIONS [ status_key ] [ " ru " ] , " language_code " : " ru " } ,
]
self . description = format_multilingual_field ( self . description )
self . short_description = format_multilingual_field ( self . short_description )
# Process timing (translate weekdays)
import json
raw_timings = [ ]
val = self . timing
if val :
if isinstance ( val , str ) :
try :
val = json . loads ( val )
except Exception :
pass
if isinstance ( val , list ) and all ( isinstance ( x , dict ) and ' language_code ' in x for x in val ) :
# We can extract the timing list from any language item
extracted = [ ]
for x in val :
title_val = x . get ( ' title ' )
if title_val :
if isinstance ( title_val , str ) :
try :
title_val = json . loads ( title_val )
except Exception :
pass
if isinstance ( title_val , list ) :
extracted = title_val
break
raw_timings = extracted
elif isinstance ( val , list ) :
raw_timings = val
elif isinstance ( val , dict ) :
raw_timings = [ ]
for k , v in val . items ( ) :
raw_timings . append ( { " day " : k , " time " : str ( v ) } )
en_timings = [ ]
ru_timings = [ ]
fa_timings = [ ]
for item in raw_timings :
if isinstance ( item , dict ) :
day_key = normalize_weekday_to_key ( item . get ( ' day ' ) )
time_val = item . get ( ' time ' ) or item . get ( ' timing ' ) or ' '
en_timings . append ( {
" day " : WEEKDAY_TRANSLATIONS . get ( day_key , { } ) . get ( ' en ' , item . get ( ' day ' , ' ' ) ) ,
" time " : time_val
} )
ru_timings . append ( {
" day " : WEEKDAY_TRANSLATIONS . get ( day_key , { } ) . get ( ' ru ' , item . get ( ' day ' , ' ' ) ) ,
" time " : time_val
} )
fa_timings . append ( {
" day " : WEEKDAY_TRANSLATIONS . get ( day_key , { } ) . get ( ' fa ' , item . get ( ' day ' , ' ' ) ) ,
" time " : time_val
} )
self . timing = [
{ " language_code " : " fa " , " title " : json . dumps ( fa_timings ) } ,
{ " language_code " : " en " , " title " : json . dumps ( en_timings ) } ,
{ " language_code " : " ru " , " title " : json . dumps ( ru_timings ) } ,
]
self . features = format_multilingual_field ( self . features )
self . features = format_multilingual_field ( self . features )
# Ensure consistency: if price is 0, set is_free to True and discount_percentage to 0
# Ensure consistency: if price is 0, set is_free to True and discount_percentage to 0