Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions internal_apis/main.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from flask import jsonify

from SLL import create_app
from config_module import get_config

Expand Down
33 changes: 33 additions & 0 deletions internal_apis/professor_endpoint/professor_endpoint.py
Original file line number Diff line number Diff line change
@@ -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"])
@cache.cached(timeout=43200, query_string=True)
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
84 changes: 71 additions & 13 deletions internal_apis/src/BLL/schema.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
"""
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.
The `Query` class specifies root-level queries. The `Mutation` class defines root-level mutations, such as `create_offer`.
"""

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):
"""
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
"""

Expand Down Expand Up @@ -248,6 +259,7 @@ class Query(ObjectType):
skip=graphene.Int(),
teacher_id=graphene.String(),
searchCourse=graphene.String(),

)
offers = List(
OfferType,
Expand Down Expand Up @@ -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) \
Expand All @@ -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) \
Expand Down Expand Up @@ -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()
Expand 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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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.
"""

Expand Down Expand Up @@ -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
Expand All @@ -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):
"""
Expand All @@ -539,3 +596,4 @@ class Mutation(ObjectType):
create_offer (CreateOffer): Mutation to create a new Offer.
"""
create_offer = CreateOffer.Field()
deactivate_teacher = DeactivateTeacher.Field()
7 changes: 5 additions & 2 deletions internal_apis/src/DAL/models.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -106,4 +110,3 @@ class Type(Document):
collection = StringField(required=True, db_field='collection')



5 changes: 2 additions & 3 deletions internal_apis/src/SLL/restapi.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -8,7 +6,7 @@
from .rooms_routes import rooms_bp
from .teachers_routes import teachers_bp
from .types_routes import types_bp

from internal_apis.professor_endpoint.professor_endpoint import deactivate_teacher_bp

def setup_rest_routes(app):

Expand All @@ -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/")



16 changes: 14 additions & 2 deletions internal_apis/src/SLL/teachers_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)

Expand All @@ -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:
Expand All @@ -71,6 +76,7 @@ def pagination_config(base_queryset):

return result, page, pagesize


def format_teacher_response(base_queryset):
total_count = base_queryset.count()

Expand All @@ -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
]
Expand All @@ -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