Skip to content

Commit

Permalink
Merge branch 'development' into backend/feature/root-endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
AronBuzogany committed Feb 23, 2024
2 parents 7dec852 + bebf79a commit 4477216
Show file tree
Hide file tree
Showing 19 changed files with 500 additions and 12 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ jobs:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4

- name: Cache dependencies
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
Expand All @@ -37,23 +37,23 @@ jobs:
working-directory: ./frontend
run: npm run lint
Backend-tests:
runs-on: self-hosted
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.x'
cache: 'pip'

- name: Install dependencies
working-directory: ./backend
run: pip3 install -r requirements.txt && pip3 install -r dev-requirements.txt

- name: Running tests
working-directory: ./backend
run: pytest
run: bash ./run_tests.sh

- name: Run linting
working-directory: ./backend
Expand Down
3 changes: 2 additions & 1 deletion backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ __pycache__/
htmlcov/
docs/_build/
dist/
venv/
venv/
.env
15 changes: 15 additions & 0 deletions backend/Dockerfile.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM python:3.9-slim

# Set the working directory
WORKDIR /app

# Copy the application code into the container
COPY . /app

# Install dependencies
RUN apt-get update
RUN apt-get install -y --no-install-recommends python3-pip
RUN pip3 install --no-cache-dir -r requirements.txt -r dev-requirements.txt

# Command to run the tests
CMD ["pytest"]
19 changes: 18 additions & 1 deletion backend/project/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,33 @@
"""

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from .endpoints.index.index import index_bp

db = SQLAlchemy()

def create_app():
"""
Create a Flask application instance.
Returns:
Flask -- A Flask application instance
"""
app = Flask(__name__)

app = Flask(__name__)
app.register_blueprint(index_bp)

return app

def create_app_with_db(db_uri:str):
"""
Initialize the database with the given uri
and connect it to the app made with create_app.
Parameters:
db_uri (str): The URI of the database to initialize.
Returns:
Flask -- A Flask application instance
"""
app = create_app()
app.config["SQLALCHEMY_DATABASE_URI"] = db_uri
db.init_app(app)
return app
7 changes: 5 additions & 2 deletions backend/project/__main__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""Main entry point for the application."""
from sys import path
from os import getenv
from dotenv import load_dotenv
from project import create_app_with_db

path.append(".")

if __name__ == "__main__":
from project import create_app
app = create_app()
load_dotenv()
app = create_app_with_db(getenv("DB_HOST"))
app.run(debug=True)
10 changes: 8 additions & 2 deletions backend/project/endpoints/index/index.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Index api point"""
from flask import Blueprint, send_from_directory
from flask_restful import Resource, Api
import os
Expand All @@ -6,8 +7,13 @@
index_endpoint = Api(index_bp)

class Index(Resource):
"""Api endpoint for the / route"""

def get(self):
"""Example of an api endpoint function that will respond to get requests made to /
return a json data structure with key Message and value Hello World!"""
dir_path = os.path.dirname(os.path.realpath(__file__))
return send_from_directory(dir_path, "OpenAPI_Object.json")

index_bp.add_url_rule("/", view_func=Index.as_view("index"))


index_bp.add_url_rule("/", view_func=Index.as_view("index"))
Empty file.
31 changes: 31 additions & 0 deletions backend/project/models/course_relations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Models for relation between users and courses"""
# pylint: disable=too-few-public-methods

from sqlalchemy import Integer, Column, ForeignKey, PrimaryKeyConstraint, String
from project import db
from project.models.users import Users
from project.models.courses import Courses

class BaseCourseRelation(db.Model):
"""Base class for course relation models,
both course relation tables have a
course_id of the course to wich someone is related and
an uid of the related person"""

__abstract__ = True

course_id = Column(Integer, ForeignKey('courses.course_id'), nullable=False)
uid = Column(String(255), ForeignKey("users.uid"), nullable=False)
__table_args__ = (
PrimaryKeyConstraint("course_id", "uid"),
)

class CourseAdmins(BaseCourseRelation):
"""Admin to course relation model"""

__tablename__ = "course_admins"

class CourseStudents(BaseCourseRelation):
"""Student to course relation model"""

__tablename__ = "course_students"
15 changes: 15 additions & 0 deletions backend/project/models/courses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""The Courses model"""
# pylint: disable=too-few-public-methods
from sqlalchemy import Integer, Column, ForeignKey, String
from project import db
from project.models.users import Users

class Courses(db.Model):
"""This class described the courses table,
a course has an id, name, optional ufora id and the teacher that created it"""

__tablename__ = "courses"
course_id = Column(Integer, primary_key=True)
name = Column(String(50), nullable=False)
ufora_id = Column(String(50), nullable=True)
teacher = Column(String(255), ForeignKey("users.uid"), nullable=False)
28 changes: 28 additions & 0 deletions backend/project/models/projects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Model for projects"""
# pylint: disable=too-few-public-methods
from sqlalchemy import ARRAY, Boolean, Column, DateTime, ForeignKey, Integer, String, Text
from project import db
from project.models.courses import Courses

class Projects(db.Model):
"""This class describes the projects table,
a projects has an id, a title, a description,
an optional assignment file that can contain more explanation of the projects,
an optional deadline,
the course id of the course to which the project belongs,
visible for students variable so a teacher can decide if the students can see it yet,
archieved var so we can implement the archiving functionality,
a test path,script name and regex experssions for automated testing"""

__tablename__ = "projects"
project_id = Column(Integer, primary_key=True)
title = Column(String(50), nullable=False, unique=False)
descriptions = Column(Text, nullable=False)
assignment_file = Column(String(50))
deadline = Column(DateTime(timezone=True))
course_id = Column(Integer, ForeignKey("courses.course_id"), nullable=False)
visible_for_students = Column(Boolean, nullable=False)
archieved = Column(Boolean, nullable=False)
test_path = Column(String(50))
script_name = Column(String(50))
regex_expressions = Column(ARRAY(String(50)))
25 changes: 25 additions & 0 deletions backend/project/models/submissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Model for submissions"""
# pylint: disable=too-few-public-methods
from sqlalchemy import Column,String,ForeignKey,Integer,CheckConstraint,DateTime,Boolean
from project import db
from project.models.users import Users

class Submissions(db.Model):
"""This class describes the submissions table,
submissions can be made to a project, a submission has
and id, a uid from the user that uploaded it,
the project id of the related project,
an optional grading,
the submission time,
submission path,
and finally the submission status
so we can easily present in a list which submission succeeded the automated checks"""

__tablename__ = "submissions"
submission_id = Column(Integer, nullable=False, primary_key=True)
uid = Column(String(255), ForeignKey("users.uid"), nullable=False)
project_id = Column(Integer, ForeignKey("projects.project_id"), nullable=False)
grading = Column(Integer, CheckConstraint("grading >= 0 AND grading <= 20"))
submission_time = Column(DateTime(timezone=True), nullable=False)
submission_path = Column(String(50), nullable=False)
submission_status = Column(Boolean, nullable=False)
16 changes: 16 additions & 0 deletions backend/project/models/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Model for users"""
# pylint: disable=too-few-public-methods
from sqlalchemy import Boolean, Column, String
from project import db


class Users(db.Model):
"""This class defines the users table,
a user has an uid,
is_teacher and is_admin booleans because a user
can be either a student,admin or teacher"""

__tablename__ = "users"
uid = Column(String(255), primary_key=True)
is_teacher = Column(Boolean)
is_admin = Column(Boolean)
3 changes: 3 additions & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
flask
flask-restful
flask-sqlalchemy
python-dotenv
psycopg2-binary
20 changes: 20 additions & 0 deletions backend/run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash

# Run Docker Compose to build and start the services, and capture the exit code from the test runner service
docker-compose -f tests.yaml up --build --exit-code-from test-runner

# Store the exit code in a variable
exit_code=$?

# After the tests are finished, stop and remove the containers
docker-compose -f tests.yaml down

# Check the exit code to determine whether the tests passed or failed
if [ $exit_code -eq 0 ]; then
echo "Tests passed!"
else
echo "Tests failed!"
fi

# Exit with the same exit code as the test runner service
exit $exit_code
31 changes: 31 additions & 0 deletions backend/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
version: '3.8'

services:
postgres:
image: postgres:latest
environment:
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
POSTGRES_DB: test_database
healthcheck:
test: ["CMD-SHELL", "pg_isready -U test_user -d test_database"]
interval: 5s
timeout: 3s
retries: 3
start_period: 5s

test-runner:
build:
context: .
dockerfile: Dockerfile.test
depends_on:
postgres:
condition: service_healthy
environment:
POSTGRES_HOST: postgres # Use the service name defined in Docker Compose
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
POSTGRES_DB: test_database
volumes:
- .:/app
command: ["pytest"]
Loading

0 comments on commit 4477216

Please sign in to comment.