import json import logging from odoo import http from odoo.http import request from odoo.addons.encoach_api.controllers.base import ( jwt_required, _json_response, _error_response, _get_json_body, _paginate ) _logger = logging.getLogger(__name__) TAXONOMY_MODELS = { 'subject': 'encoach.subject', 'domain': 'encoach.domain', 'topic': 'encoach.topic', 'learning_objective': 'encoach.learning.objective', } PARENT_FIELD_MAP = { 'domain': 'subject_id', 'topic': 'domain_id', 'learning_objective': 'topic_id', } class EncoachTaxonomyController(http.Controller): # ------------------------------------------------------------------ # GET /api/taxonomy/subjects # ------------------------------------------------------------------ @http.route('/api/taxonomy/subjects', type='http', auth='none', methods=['GET'], csrf=False) @jwt_required def get_subjects(self, **kw): try: Subject = request.env['encoach.subject'].sudo() subjects = Subject.search([('active', '=', True)]) items = [] for s in subjects: items.append({ 'id': s.id, 'name': s.name, 'code': s.code or '', 'description': s.description or '', 'domain_count': len(s.domain_ids), }) return _json_response({'subjects': items}) except Exception as e: _logger.exception('get_subjects failed') return _error_response(str(e), 500) # ------------------------------------------------------------------ # GET /api/taxonomy/tree # ------------------------------------------------------------------ @http.route('/api/taxonomy/tree', type='http', auth='none', methods=['GET'], csrf=False) @jwt_required def get_tree(self, **kw): try: Subject = request.env['encoach.subject'].sudo() subjects = Subject.search([('active', '=', True)]) tree = [] for s in subjects: domains = [] for d in s.domain_ids: topics = [] for t in d.topic_ids: objectives = [] for o in t.learning_objective_ids: objectives.append({ 'id': o.id, 'name': o.name, 'bloom_level': o.bloom_level or '', 'description': o.description or '', }) topics.append({ 'id': t.id, 'name': t.name, 'description': t.description or '', 'learning_objectives': objectives, }) domains.append({ 'id': d.id, 'name': d.name, 'description': d.description or '', 'topics': topics, }) tree.append({ 'id': s.id, 'name': s.name, 'code': s.code or '', 'description': s.description or '', 'domains': domains, }) return _json_response({'tree': tree}) except Exception as e: _logger.exception('get_tree failed') return _error_response(str(e), 500) # ------------------------------------------------------------------ # POST /api/taxonomy/node # ------------------------------------------------------------------ @http.route('/api/taxonomy/node', type='http', auth='none', methods=['POST'], csrf=False) @jwt_required def create_node(self, **kw): try: body = _get_json_body() node_type = body.get('type') if not node_type or node_type not in TAXONOMY_MODELS: return _error_response( f'type must be one of: {", ".join(TAXONOMY_MODELS.keys())}', 400, ) name = body.get('name') if not name: return _error_response('name is required', 400) model_name = TAXONOMY_MODELS[node_type] Model = request.env[model_name].sudo() vals = {'name': name} if body.get('description'): vals['description'] = body['description'] if node_type == 'subject': code = body.get('code') if not code: return _error_response('code is required for subjects', 400) vals['code'] = code else: parent_id = body.get('parent_id') if not parent_id: parent_field = PARENT_FIELD_MAP[node_type] return _error_response( f'parent_id ({parent_field}) is required for {node_type}', 400, ) vals[PARENT_FIELD_MAP[node_type]] = int(parent_id) if node_type == 'learning_objective' and body.get('bloom_level'): vals['bloom_level'] = body['bloom_level'] record = Model.create(vals) return _json_response({ 'id': record.id, 'type': node_type, 'name': record.name, }, 201) except Exception as e: _logger.exception('create_node failed') return _error_response(str(e), 500) # ------------------------------------------------------------------ # PUT /api/taxonomy/node/ # ------------------------------------------------------------------ @http.route('/api/taxonomy/node/', type='http', auth='none', methods=['PUT'], csrf=False) @jwt_required def update_node(self, node_id, **kw): try: body = _get_json_body() node_type = body.get('type') if not node_type or node_type not in TAXONOMY_MODELS: return _error_response( f'type must be one of: {", ".join(TAXONOMY_MODELS.keys())}', 400, ) model_name = TAXONOMY_MODELS[node_type] Model = request.env[model_name].sudo() record = Model.browse(node_id) if not record.exists(): return _error_response(f'{node_type} not found', 404) vals = {} if body.get('name'): vals['name'] = body['name'] if 'description' in body: vals['description'] = body.get('description') or '' if node_type == 'subject': if body.get('code'): vals['code'] = body['code'] if 'active' in body: vals['active'] = bool(body['active']) elif node_type == 'learning_objective' and body.get('bloom_level'): vals['bloom_level'] = body['bloom_level'] if vals: record.write(vals) return _json_response(record.to_api_dict()) except Exception as e: _logger.exception('update_node failed') return _error_response(str(e), 500) # ------------------------------------------------------------------ # DELETE /api/taxonomy/node/ # ------------------------------------------------------------------ @http.route('/api/taxonomy/node/', type='http', auth='none', methods=['DELETE'], csrf=False) @jwt_required def delete_node(self, node_id, **kw): try: body = _get_json_body() node_type = body.get('type') or kw.get('type') if not node_type or node_type not in TAXONOMY_MODELS: return _error_response( f'type must be one of: {", ".join(TAXONOMY_MODELS.keys())}', 400, ) model_name = TAXONOMY_MODELS[node_type] Model = request.env[model_name].sudo() record = Model.browse(node_id) if not record.exists(): return _error_response(f'{node_type} not found', 404) record.unlink() return _json_response({}, 204) except Exception as e: _logger.exception('delete_node failed') return _error_response(str(e), 500)