Skip to content

Commit 3d75202

Browse files
Merge branch '296_critetion_to_web'
2 parents f413932 + 4fd7ed9 commit 3d75202

File tree

27 files changed

+503
-53
lines changed

27 files changed

+503
-53
lines changed

app/api/criteria.py

Lines changed: 121 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,109 @@
1-
import logging
2-
31
from bson import ObjectId
4-
from flask import Blueprint
2+
from flask import Blueprint, request
3+
import logging
4+
from json import dumps
55

66
from app.check_access import check_access
7+
from app.criteria import CRITERIONS
8+
from app.criteria.utils import create_criterion
79
from app.criteria_pack import CriteriaPackFactory
810
from app.lti_session_passback.auth_checkers import is_admin
9-
from app.mongo_odm import TrainingsDBManager
10-
from app.utils import check_argument_is_convertible_to_object_id
11+
from app.mongo_models import Criterion
12+
from app.mongo_odm import TrainingsDBManager, CriterionDBManager
13+
from app.root_logger import get_root_logger
14+
from app.utils import check_argument_is_convertible_to_object_id, remove_blank_and_none, try_load_json, check_dict_keys
15+
16+
17+
api_criteria = Blueprint('api_criteria', __name__, url_prefix="/api/criterion")
18+
logger = get_root_logger('web')
19+
20+
21+
@api_criteria.route('/create/', methods=['POST'])
22+
def create_new_criterion():
23+
if not is_admin():
24+
return {}, 404
25+
26+
return update_criterion('')
27+
28+
29+
@api_criteria.route('/<criterion_name>/', methods=['GET'])
30+
def get_criterion(criterion_name):
31+
if not check_access():
32+
return {}, 404
33+
db_criterion = CriterionDBManager().get_criterion_by_name(criterion_name)
34+
if db_criterion:
35+
return db_criterion.to_dict()
36+
else:
37+
return {}, 200
38+
39+
40+
@api_criteria.route('/<criterion_name>/', methods=['POST'])
41+
def update_criterion(criterion_name):
42+
if not is_admin():
43+
return {}, 404
44+
45+
criterion_dict, msg = try_load_criterion_dict(
46+
request.form.get('parameters'))
47+
if msg:
48+
return {'message': msg}, 200
49+
50+
base_criterion_name = request.form.get('base_criterion', '')
51+
base_criterion = CRITERIONS.get(base_criterion_name)
52+
if not base_criterion:
53+
return {'message': f"No such base critetion '{base_criterion_name}'"}, 200
54+
55+
db_criterion = CriterionDBManager().get_criterion_by_name(
56+
criterion_dict['name'])
57+
if not db_criterion:
58+
db_criterion = Criterion(name=criterion_dict['name'], parameters={},
59+
base_criterion=base_criterion_name)
1160

12-
api_criteria = Blueprint('api_criteria', __name__)
13-
logger = logging.getLogger('root_logger')
61+
instance, msg = check_criterion_dict(base_criterion, criterion_dict)
62+
if msg:
63+
return {'message': msg}, 200
1464

65+
db_criterion.parameters = instance.dict.get('parameters')
66+
db_criterion.base_criterion = base_criterion_name
67+
db_criterion.save()
68+
logger.info(f"Updated criterion {db_criterion.name}")
69+
return {
70+
'message': 'OK',
71+
'name': db_criterion.name,
72+
'time': int(db_criterion.last_update.timestamp()*1000)
73+
}, 200
1574

16-
@api_criteria.route('/api/criteria/<training_id>/<criterion_name>/<parameter_name>/', methods=['GET'])
75+
76+
@api_criteria.route('/<criterion_name>/structure/', methods=['GET'])
77+
def get_criteria_structure(criterion_name):
78+
if not check_access():
79+
return {}, 404
80+
criterion = CRITERIONS.get(criterion_name)
81+
if create_criterion:
82+
return criterion.structure()
83+
else:
84+
return {}, 404
85+
86+
@api_criteria.route('/list/', methods=['GET'])
87+
def get_all_criterions():
88+
if not is_admin():
89+
return {}, 404
90+
91+
criterions = CriterionDBManager().get_all_criterions()
92+
return {
93+
'criterions': criterions,
94+
'message': 'OK'
95+
}
96+
97+
98+
@api_criteria.route('/structures', methods=['GET'])
99+
def get_all_criterion_structures():
100+
if not is_admin():
101+
return {}, 404
102+
103+
return {name: dumps(criterion.structure(), indent=3) for name, criterion in CRITERIONS.items()}
104+
105+
106+
@api_criteria.route('/<training_id>/<criterion_name>/<parameter_name>/', methods=['GET'])
17107
def get_criterion_parameter_value(training_id: str, criterion_name, parameter_name) -> (dict, int):
18108
"""
19109
Endpoint to retrieve criterion parameter value.
@@ -39,3 +129,26 @@ def get_criterion_parameter_value(training_id: str, criterion_name, parameter_na
39129
if parameter_value is None:
40130
return {'message': 'No parameter with name = {}.'.format(parameter_name)}, 404
41131
return {'parameterName': parameter_name, 'parameterValue': parameter_value, 'message': 'OK'}, 200
132+
133+
134+
def check_criterion_dict(base_criterion, criterion_dict):
135+
instance, msg = create_criterion(base_criterion, criterion_dict)
136+
# try to get criterion's description
137+
if not instance:
138+
return False, f"Error on creating instance of {base_criterion.__name__} with params {criterion_dict['parameters']} for new criterion '{criterion_dict['name']}'.<br>{msg}"
139+
return instance, ''
140+
141+
142+
def try_load_criterion_dict(json_str):
143+
criterion_dict, msg = try_load_json(json_str)
144+
if msg:
145+
return False, msg # error on parsing
146+
147+
criterion_dict = remove_blank_and_none(criterion_dict)
148+
149+
msg = check_dict_keys(
150+
criterion_dict, ('name', 'parameters'))
151+
if msg:
152+
return False, msg # error with dict keys
153+
154+
return criterion_dict, ''

app/criteria/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class EmptyCriterion:
1919
2020
def __init__(self, parameters: dict, dependent_criteria: list):
2121
super().__init__(
22+
name=name,
2223
parameters=parameters,
2324
dependent_criteria=dependent_criteria,
2425
)
@@ -34,8 +35,7 @@ class EmptyCriterion:
3435
## Правила реализации и работы с модулями
3536
- Все файлы, необходимые для критерия, размещаются в директории с именем, соответствующем названию критерия в стиле `lower_case_with_underscores`.
3637
- Класс критерия размещается в файле `criterion.py`
37-
- Для возможности импорта критерия в других модулях системы, необходимо разместить импорт класс критерия в `__init__.py`
38-
- Также для корректной работы системы необходимо внести класс критерия по ключу, соответствующему названию класса, в `CRITERIONS` в том же модуле.
38+
- Для возможности импорта критерия в других модулях системы, необходимо разместить импорт класса критерия в `critetions.py`
3939
- Внешние модули должны знать только классы критериев - никакие другие функции/классы/переменные не импортируются и не выносятся
4040
```
4141
from .empty_criterion.criterion import EmptyCriterion

app/criteria/__init__.py

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,7 @@
1-
from .fillers_number.criterion import FillersNumberCriterion
2-
from .fillers_ratio.criterion import FillersRatioCriterion
3-
from .len_text_on_slide.critetion import LenTextOnSlideCriterion
4-
from .number_slides.critetion import NumberSlidesCriterion
5-
from .number_word_on_slide.critetion import NumberWordOnSlideCriterion
6-
from .speech_duration.criterion import SpeechDurationCriterion
7-
from .speech_is_not_in_database.criterion import SpeechIsNotInDatabaseCriterion
8-
from .speech_pace.criterion import SpeechPaceCriterion
9-
from .strict_speech_duration.criterion import StrictSpeechDurationCriterion
1+
import sys, inspect
102

11-
CRITERIONS = {
12-
FillersNumberCriterion.__name__: FillersNumberCriterion,
13-
FillersRatioCriterion.__name__: FillersRatioCriterion,
14-
LenTextOnSlideCriterion.__name__: LenTextOnSlideCriterion,
15-
NumberSlidesCriterion.__name__: NumberSlidesCriterion,
16-
NumberWordOnSlideCriterion.__name__: NumberWordOnSlideCriterion,
17-
SpeechDurationCriterion.__name__: SpeechDurationCriterion,
18-
SpeechIsNotInDatabaseCriterion.__name__: SpeechIsNotInDatabaseCriterion,
19-
SpeechPaceCriterion.__name__: SpeechPaceCriterion,
20-
StrictSpeechDurationCriterion.__name__: StrictSpeechDurationCriterion,
21-
}
3+
from .criterions import *
4+
from .utils import check_criterions, create_criterion
225

236

24-
from .utils import check_criterions
7+
CRITERIONS = dict(inspect.getmembers(sys.modules[__name__], inspect.isclass))

app/criteria/criterion_base.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ class Criterion:
88

99
PARAMETERS = dict()
1010

11-
def __init__(self, parameters: dict, dependent_criteria: list):
12-
self.name = self.__class__.__name__
11+
def __init__(self, parameters: dict, dependent_criteria: list, name=''):
12+
self.name = name if name else self.__class__.__name__
1313
self.parameters = parameters
1414
self.dependent_criteria = dependent_criteria
1515

@@ -21,8 +21,15 @@ def apply(self, audio: Audio, presentation: Presentation, training_id: ObjectId,
2121
-> CriterionResult:
2222
raise NotImplementedError()
2323

24+
@property
25+
def dict(self) -> dict:
26+
return dict(
27+
name = self.name,
28+
parameters = self.parameters
29+
)
30+
2431
@classmethod
25-
def structure_to_json(cls):
32+
def structure(cls) -> dict:
2633
# for simplicity, dependent criteria are removed
2734
return dict(
2835
name=cls.__name__,
@@ -32,4 +39,4 @@ def structure_to_json(cls):
3239
@classmethod
3340
def from_dict(cls, dictionary):
3441
dependent_criterions = [] # for simplicity, dependent criteria are removed
35-
return cls(dictionary['parameters'], dependent_criterions)
42+
return cls(dictionary['parameters'], dependent_criterions, name=dictionary.get('name'))

app/criteria/criterions.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from .fillers_number.criterion import FillersNumberCriterion
2+
from .fillers_ratio.criterion import FillersRatioCriterion
3+
from .len_text_on_slide.critetion import LenTextOnSlideCriterion
4+
from .number_slides.critetion import NumberSlidesCriterion
5+
from .number_word_on_slide.critetion import NumberWordOnSlideCriterion
6+
from .speech_duration.criterion import SpeechDurationCriterion
7+
from .speech_is_not_in_database.criterion import SpeechIsNotInDatabaseCriterion
8+
from .speech_pace.criterion import SpeechPaceCriterion
9+
from .strict_speech_duration.criterion import StrictSpeechDurationCriterion

app/criteria/fillers_number/criterion.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ class FillersNumberCriterion(Criterion):
1111
maximum_fillers_number=int.__name__
1212
)
1313

14-
def __init__(self, parameters, dependent_criteria):
14+
def __init__(self, parameters, dependent_criteria, name=''):
1515
super().__init__(
16+
name=name,
1617
parameters=parameters,
1718
dependent_criteria=dependent_criteria,
1819
)

app/criteria/fillers_ratio/criterion.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ class FillersRatioCriterion(Criterion):
1010
fillers=list.__name__
1111
)
1212

13-
def __init__(self, parameters, dependent_criteria):
13+
def __init__(self, parameters, dependent_criteria, name=''):
1414
if 'fillers' not in parameters:
1515
raise ValueError(
1616
'parameters should contain {}.'.format('fillers'))
1717

1818
super().__init__(
19+
name=name,
1920
parameters=parameters,
2021
dependent_criteria=dependent_criteria,
2122
)

app/criteria/len_text_on_slide/critetion.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ class LenTextOnSlideCriterion(Criterion):
1212
minimal_number_words=int.__name__
1313
)
1414

15-
def __init__(self, parameters, dependent_criteria):
15+
def __init__(self, parameters, dependent_criteria, name=''):
1616
if 'minimal_number_words' not in parameters:
1717
raise ValueError(
1818
'parameters should contain \'minimal_number_words\'.')
1919
super().__init__(
20+
name=name,
2021
parameters=parameters,
2122
dependent_criteria=dependent_criteria,
2223
)

app/criteria/number_slides/critetion.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ class NumberSlidesCriterion(Criterion):
1111
maximal_allowed_slide_number=int.__name__
1212
)
1313

14-
def __init__(self, parameters, dependent_criteria):
14+
def __init__(self, parameters, dependent_criteria, name=''):
1515
if 'minimal_allowed_slide_number' not in parameters and 'maximal_allowed_slide_number' not in parameters:
1616
raise ValueError(
1717
'parameters should contain \'minimal_allowed_slide_number\' or \'maximal_allowed_slide_number\'.')
1818
super().__init__(
19+
name=name,
1920
parameters=parameters,
2021
dependent_criteria=dependent_criteria,
2122
)

app/criteria/number_word_on_slide/critetion.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ class NumberWordOnSlideCriterion(Criterion):
1010
minimal_number_words=int.__name__
1111
)
1212

13-
def __init__(self, parameters, dependent_criteria):
13+
def __init__(self, parameters, dependent_criteria, name=''):
1414
if 'minimal_number_words' not in parameters:
1515
raise ValueError(
1616
'parameters should contain \'minimal_number_words\'.')
1717
super().__init__(
18+
name=name,
1819
parameters=parameters,
1920
dependent_criteria=dependent_criteria,
2021
)

0 commit comments

Comments
 (0)