From 722e23c279f34b2d172c30f6fbead5793b9f0a00 Mon Sep 17 00:00:00 2001 From: pedruck <68859909+pedruck@users.noreply.github.com> Date: Mon, 12 May 2025 00:57:24 -0300 Subject: [PATCH 1/3] atualizando branch --- .../professor_endpoint/professor_endpoint.py | 33 ++++++++ internal_apis/src/BLL/schema.py | 84 ++++++++++++++++--- internal_apis/src/DAL/models.py | 7 +- internal_apis/src/SLL/restapi.py | 5 +- internal_apis/src/SLL/teachers_routes.py | 16 +++- 5 files changed, 125 insertions(+), 20 deletions(-) create mode 100644 internal_apis/professor_endpoint/professor_endpoint.py diff --git a/internal_apis/professor_endpoint/professor_endpoint.py b/internal_apis/professor_endpoint/professor_endpoint.py new file mode 100644 index 0000000..8974616 --- /dev/null +++ b/internal_apis/professor_endpoint/professor_endpoint.py @@ -0,0 +1,33 @@ +import requests +from flask import Blueprint, jsonify, request +import datetime +from utils.cache import cache +from BLL.schema import DeactivateTeacher +from DAL import Offer, Campus, Discipline, Period, Room, Teacher +from utils import log_info_request, log_resource_not_found, check_api_key, get_swagger_specification + +deactivate_teacher_bp = Blueprint('deactivate_teacher_bp', __name__, url_prefix="teachers/softdel") + + +@deactivate_teacher_bp.route("/", methods=["DELETE"]) +@log_info_request +def delete_item(): + teacher_name = request.args.get("teacher_name") + mutation = f''' + mutation {{ + deactivateTeacher(name:{teacher_name}) {{ + teacher {{ + id + name + + }} + }} +}} ''' + + mutresponse = (requests.post("http://127.0.0.1:5081/graphql", json={"query": mutation})) + + if mutresponse.status_code == 200: + + return jsonify(mutresponse.json()), 200 + else: + return jsonify({"error": "Erro ao executar mutation"}), 500 \ No newline at end of file diff --git a/internal_apis/src/BLL/schema.py b/internal_apis/src/BLL/schema.py index 3e19db2..fb22525 100644 --- a/internal_apis/src/BLL/schema.py +++ b/internal_apis/src/BLL/schema.py @@ -1,10 +1,10 @@ """ -This module defines a GraphQL schema for querying and mutating data related to a university-like environment. -It uses MongoEngine models to represent entities such as Campuses, Courses, Disciplines, Periods, Rooms, Teachers, Offers, and Types. -The schema includes queries for fetching lists of these entities and various search parameters. +This module defines a GraphQL schema for querying and mutating data related to a university-like environment. +It uses MongoEngine models to represent entities such as Campuses, Courses, Disciplines, Periods, Rooms, Teachers, Offers, and Types. +The schema includes queries for fetching lists of these entities and various search parameters. It also provides mutations for creating Offers. -This schema leverages DataLoader-like loaders (through `info.context['loaders']`) to batch load related entities +This schema leverages DataLoader-like loaders (through `info.context['loaders']`) to batch load related entities (e.g., courses associated with a discipline, or the campus associated with a room), improving query efficiency. Classes ending with `Type` are GraphQL object types corresponding to MongoEngine models or derived objects. @@ -12,12 +12,22 @@ """ import graphene -from graphene import ObjectType, List, Field, Mutation, Scalar +from graphene import ObjectType, List, Field, Mutation, Scalar, String, Boolean from graphene_mongo import MongoengineObjectType import datetime +import os + +from graphql import GraphQLError +from pymongo import MongoClient from DAL import Campus, Course, Discipline, Period, Room, Teacher, Offer, Type +mongodb_uri = os.getenv("MONGO_URI") +database_name = os.getenv("MONGO_DATABASE") +client = MongoClient(mongodb_uri) +database = client[database_name] + + class CampusType(MongoengineObjectType): """ @@ -118,6 +128,7 @@ class TeacherType(MongoengineObjectType): Additional fields come directly from the `Teacher` model. """ course = List(CourseType) + soft_deleted = graphene.String() class Meta: model = Teacher @@ -213,7 +224,7 @@ class Query(ObjectType): """ The root Query object for the GraphQL schema. - Provides various query fields to fetch lists of campuses, courses, disciplines, periods, rooms, teachers, offers, and types. + Provides various query fields to fetch lists of campuses, courses, disciplines, periods, rooms, teachers, offers, and types. Includes search, pagination (first, skip), and filtering (by ID or name) capabilities. """ @@ -248,6 +259,7 @@ class Query(ObjectType): skip=graphene.Int(), teacher_id=graphene.String(), searchCourse=graphene.String(), + ) offers = List( OfferType, @@ -279,7 +291,7 @@ def resolve_get_campus_by_id(root, info, id): def resolve_courses(root, info, search=None, first=None, skip=None, course_id=None): """ - Return a list of courses. Supports filtering by name (search), limiting (first), + Return a list of courses. Supports filtering by name (search), limiting (first), skipping (skip), and filtering by specific course_id. """ query = Course.objects.filter(name__icontains=search) \ @@ -298,7 +310,7 @@ def resolve_courses(root, info, search=None, first=None, skip=None, course_id=No def resolve_disciplines(root, info, search=None, first=None, skip=None, discipline_id=None, searchCourse=None): """ - Return a list of disciplines. Supports filtering by name (search), limiting (first), + Return a list of disciplines. Supports filtering by name (search), limiting (first), skipping (skip), filtering by discipline_id, and searching by associated course name (searchCourse). """ query = Discipline.objects.filter(name__icontains=search) \ @@ -328,7 +340,7 @@ def resolve_periods(root, info): def resolve_rooms(root, info, search=None, first=None, skip=None, room_id=None): """ - Return a list of rooms. Supports filtering by name (search), limiting (first), + Return a list of rooms. Supports filtering by name (search), limiting (first), skipping (skip), and filtering by room_id. """ query = Room.objects.filter(name__icontains=search) if search else Room.objects.all() @@ -346,7 +358,7 @@ def resolve_rooms(root, info, search=None, first=None, skip=None, room_id=None): def resolve_teachers(root, info, search=None, first=None, skip=None, teacher_id=None, searchCourse=None): """ - Return a list of teachers. Supports filtering by name (search), limiting (first), + Return a list of teachers. Supports filtering by name (search), limiting (first), skipping (skip), filtering by teacher_id, and searching by associated course name (searchCourse). """ query = Teacher.objects.filter(name__icontains=search) if search else Teacher.objects.all() @@ -377,7 +389,7 @@ def resolve_offers( searchSemester=None, searchYear=None ): """ - Return a list of offers. Supports filtering by related entities' names + Return a list of offers. Supports filtering by related entities' names (campus, discipline, period, room, teacher) and paging (first, skip). """ query = Offer.objects.all() @@ -435,7 +447,7 @@ def resolve_types(root, info, search=None): class IntOrString(Scalar): """ - A custom scalar that may represent either an Int or a String. + A custom scalar that may represent either an Int or a String. Useful in cases where an ID field might be numeric or a string. """ @@ -503,7 +515,7 @@ class Arguments: def mutate(self, info, offer_data): """ - Create and save a new Offer using the provided input. + Create and save a new Offer using the provided input. Resolved entities (discipline, period, campus, room, teacher) are loaded from the DataLoader. """ loader = info.context['loaders']['context-loader'].offer_loader @@ -530,6 +542,51 @@ def mutate(self, info, offer_data): return CreateOffer(offer=offer) +class DeactivateTeacher(graphene.Mutation): + + class Arguments: + name = graphene.String() + + success = graphene.Boolean() + teacher = graphene.Field(TeacherType) + + + + + def mutate(self, info, name): + + teacher_list = Teacher.objects.filter(name__icontains=name) + teacher_obj = teacher_list.first() + if not teacher_obj: + raise GraphQLError( + f"Teacher with ID {name} not found.") + time = datetime.datetime.now() + timestr = time.strftime('%Y-%m-%d %H:%M:%S') + + teacher_obj.update(soft_deleted= timestr) + teacher_obj.save() + + + return DeactivateTeacher( + + success=True, + teacher=teacher_obj + + ) + + + + + + + + + + + + + + class Mutation(ObjectType): """ @@ -539,3 +596,4 @@ class Mutation(ObjectType): create_offer (CreateOffer): Mutation to create a new Offer. """ create_offer = CreateOffer.Field() + deactivate_teacher = DeactivateTeacher.Field() diff --git a/internal_apis/src/DAL/models.py b/internal_apis/src/DAL/models.py index c6e8671..6f1439e 100644 --- a/internal_apis/src/DAL/models.py +++ b/internal_apis/src/DAL/models.py @@ -1,4 +1,6 @@ -from mongoengine import Document, StringField, IntField, ListField, EmbeddedDocumentField, EmbeddedDocument +from graphene import Boolean +from mongoengine import Document, StringField, IntField, ListField, EmbeddedDocumentField, EmbeddedDocument, \ + BooleanField class Campus(Document): @@ -48,6 +50,8 @@ class Teacher(Document): name = StringField(required=True, db_field='PROFESSOR') course = ListField(IntField(), required=True, db_field='COD_CURS') email = StringField(required=False, db_field='email') + active = BooleanField(required=True, db_field='ACTIVE') + soft_deleted = StringField(required=False, db_field='soft_deleted') class Offer(Document): @@ -106,4 +110,3 @@ class Type(Document): collection = StringField(required=True, db_field='collection') - diff --git a/internal_apis/src/SLL/restapi.py b/internal_apis/src/SLL/restapi.py index 5b95dec..856db4a 100644 --- a/internal_apis/src/SLL/restapi.py +++ b/internal_apis/src/SLL/restapi.py @@ -1,5 +1,3 @@ -from flasgger import Swagger - from .campus_routes import campus_bp from .courses_routes import courses_bp from .disciplines_routes import disciplines_bp @@ -8,7 +6,7 @@ from .rooms_routes import rooms_bp from .teachers_routes import teachers_bp from .types_routes import types_bp - +from professor_endpoint.professor_endpoint import deactivate_teacher_bp def setup_rest_routes(app): @@ -20,6 +18,7 @@ def setup_rest_routes(app): app.register_blueprint(rooms_bp, url_prefix="/restapi/rooms") app.register_blueprint(teachers_bp, url_prefix="/restapi/teachers") app.register_blueprint(types_bp, url_prefix="/restapi/types/") + app.register_blueprint(deactivate_teacher_bp, url_prefix="/restapi/teachers/softdel") diff --git a/internal_apis/src/SLL/teachers_routes.py b/internal_apis/src/SLL/teachers_routes.py index e6cc89e..a10ed11 100644 --- a/internal_apis/src/SLL/teachers_routes.py +++ b/internal_apis/src/SLL/teachers_routes.py @@ -8,10 +8,12 @@ teachers_bp = Blueprint('teachers_bp', __name__, url_prefix="/teachers") spec = get_swagger_specification(path="teachers", method="GET") + + @teachers_bp.route("/", methods=["GET"]) @log_info_request @swag_from(spec) -@cache.cached(timeout=43200,query_string=True) # Cache for 12 hours +@cache.cached(timeout=43200, query_string=True) # Cache for 12 hours def get_teachers(): teacher_id = request.args.get("teacher_id") if teacher_id: @@ -36,7 +38,7 @@ def get_teachers(): except Teacher.DoesNotExist: log_resource_not_found("Teacher", "course_id", course_id) return jsonify({"error": "Teacher not found"}), 404 - + teachers_queryset = Teacher.objects() return format_teacher_response(teachers_queryset) @@ -49,13 +51,16 @@ def get_teacher_by_id(teacher_id): "name": result.name, "course_id": result.course, "email": result.email if result.email else None, + }) + def filter_teacher_by_name(teacher_name): result = Teacher.objects.filter(name__icontains=teacher_name) if result: return format_teacher_response(result) + def filter_teacher_by_course_id(course_id): result = Teacher.objects.filter(course=course_id) if result: @@ -71,6 +76,7 @@ def pagination_config(base_queryset): return result, page, pagesize + def format_teacher_response(base_queryset): total_count = base_queryset.count() @@ -82,6 +88,7 @@ def format_teacher_response(base_queryset): "name": r.name, "course": r.course, "email": r.email if r.email else None, + "active": r.active } for r in result ] @@ -96,3 +103,8 @@ def format_teacher_response(base_queryset): "total_pages": total_pages } }) + + +import flask_caching +from flasgger import swag_from +from flask import Blueprint, jsonify, request From 7a1e777470af17632ad287bf814195bdfd600da8 Mon Sep 17 00:00:00 2001 From: pedruck <68859909+pedruck@users.noreply.github.com> Date: Mon, 12 May 2025 14:52:40 -0300 Subject: [PATCH 2/3] concertando bugs --- internal_apis/main.py | 2 ++ internal_apis/professor_endpoint/professor_endpoint.py | 4 ++-- internal_apis/src/SLL/restapi.py | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/internal_apis/main.py b/internal_apis/main.py index 89c8888..d29926f 100644 --- a/internal_apis/main.py +++ b/internal_apis/main.py @@ -1,3 +1,5 @@ +from flask import jsonify + from SLL import create_app from config_module import get_config diff --git a/internal_apis/professor_endpoint/professor_endpoint.py b/internal_apis/professor_endpoint/professor_endpoint.py index 8974616..9ee4318 100644 --- a/internal_apis/professor_endpoint/professor_endpoint.py +++ b/internal_apis/professor_endpoint/professor_endpoint.py @@ -6,11 +6,11 @@ from DAL import Offer, Campus, Discipline, Period, Room, Teacher from utils import log_info_request, log_resource_not_found, check_api_key, get_swagger_specification -deactivate_teacher_bp = Blueprint('deactivate_teacher_bp', __name__, url_prefix="teachers/softdel") +deactivate_teacher_bp = Blueprint('deactivate_teacher_bp', __name__, url_prefix="/softdel") @deactivate_teacher_bp.route("/", methods=["DELETE"]) -@log_info_request +@cache.cached(timeout=43200, query_string=True) def delete_item(): teacher_name = request.args.get("teacher_name") mutation = f''' diff --git a/internal_apis/src/SLL/restapi.py b/internal_apis/src/SLL/restapi.py index 856db4a..4f8e7fe 100644 --- a/internal_apis/src/SLL/restapi.py +++ b/internal_apis/src/SLL/restapi.py @@ -6,7 +6,7 @@ from .rooms_routes import rooms_bp from .teachers_routes import teachers_bp from .types_routes import types_bp -from professor_endpoint.professor_endpoint import deactivate_teacher_bp +from internal_apis.professor_endpoint.professor_endpoint import deactivate_teacher_bp def setup_rest_routes(app): @@ -18,7 +18,7 @@ def setup_rest_routes(app): app.register_blueprint(rooms_bp, url_prefix="/restapi/rooms") app.register_blueprint(teachers_bp, url_prefix="/restapi/teachers") app.register_blueprint(types_bp, url_prefix="/restapi/types/") - app.register_blueprint(deactivate_teacher_bp, url_prefix="/restapi/teachers/softdel") + app.register_blueprint(deactivate_teacher_bp, url_prefix="/restapi/softdel/") From 7c632e48f2f7dc3dc78463ac5383fac4bf8dbdf6 Mon Sep 17 00:00:00 2001 From: pedruck <68859909+pedruck@users.noreply.github.com> Date: Mon, 12 May 2025 14:55:28 -0300 Subject: [PATCH 3/3] ajustes finais para a entrega da pr --- internal_apis/professor_endpoint/professor_endpoint.py | 2 +- internal_apis/src/SLL/restapi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal_apis/professor_endpoint/professor_endpoint.py b/internal_apis/professor_endpoint/professor_endpoint.py index 9ee4318..5f04fdf 100644 --- a/internal_apis/professor_endpoint/professor_endpoint.py +++ b/internal_apis/professor_endpoint/professor_endpoint.py @@ -6,7 +6,7 @@ from DAL import Offer, Campus, Discipline, Period, Room, Teacher from utils import log_info_request, log_resource_not_found, check_api_key, get_swagger_specification -deactivate_teacher_bp = Blueprint('deactivate_teacher_bp', __name__, url_prefix="/softdel") +deactivate_teacher_bp = Blueprint('deactivate_teacher_bp', __name__, url_prefix="teachers/softdel") @deactivate_teacher_bp.route("/", methods=["DELETE"]) diff --git a/internal_apis/src/SLL/restapi.py b/internal_apis/src/SLL/restapi.py index 4f8e7fe..d527dfd 100644 --- a/internal_apis/src/SLL/restapi.py +++ b/internal_apis/src/SLL/restapi.py @@ -18,7 +18,7 @@ def setup_rest_routes(app): app.register_blueprint(rooms_bp, url_prefix="/restapi/rooms") app.register_blueprint(teachers_bp, url_prefix="/restapi/teachers") app.register_blueprint(types_bp, url_prefix="/restapi/types/") - app.register_blueprint(deactivate_teacher_bp, url_prefix="/restapi/softdel/") + app.register_blueprint(deactivate_teacher_bp, url_prefix="/restapi/teachers/softdel/")