Skip to content
Merged
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,7 @@ venv.bak/

# Remove all CSV and JSON file
*.csv
*.json
*.json

# Custom
sample.py
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

All notable changes to this project will be documented in this file.

## [2.1.0] - Jan x, 2020

### Added
- OpenAPI Swagger Documentation, available at `/api`
- New `PlagiarismSerializer`
- Email API. Currently available email APIs.
- Plagiarism Report (Report Students for plagiarism)
- Report Question (Sends e-mail to professor for improving the question with student feedback)
- Anonymous Website Feedback (Provide feedback to creators) with option for providing user e-mail.
- Token Auth

### Removed
- Old API Documentation end-point `api-docs`.


## [2.0.0] - Jan 18, 2020

### Added
Expand Down
39 changes: 39 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:

## Guidelines

Make sure you follow below guidelines before contributing.

1. Raise an issue before sending any PR.
Expand All @@ -13,3 +15,40 @@ Make sure you follow below guidelines before contributing.
7. Wait for the team to review it.

> Raise an issue, if you have any doubts :)


## Environment Keys Setup

Here is how a sample `.env` file looks like

```
SECRET_KEY = 'the-secret-key'
DEBUG = 'any valid character for setting DEBUG=False'
MOSS_ID = "moss_user_id"
user_email = "your-gmail-address"
user_pass = "your-gmail-password"
```

### Getting `MOSS_ID`

- To obtain a Moss account, send a mail message to [moss@moss.stanford.edu](moss@moss.stanford.edu). The body of the message should appear exactly as follows:

```
registeruser
mail username@domain
```

where _username@domain_ is your email address. Make sure to NOT add any styling to the message (NO signature, NO bold text etc.)
- You will recieve an email in a minute or to containig the Shell Script, look for `$userid` in the script.

### Getting `SECRET_KEY`

You can use one of the following services to generate SECRET_KEY.

- [Django Secret Key Generator](https://www.miniwebtool.com/django-secret-key-generator/)
- [Djecrety](https://djecrety.ir/)

### Setting Gmail

- Make sure to Turn ON low security apps in your Google account.
- Login and go to [this](https://myaccount.google.com/lesssecureapps) link.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<a href="https://github.com/codeclassroom/codeclassroom/releases">
<img alt="release" src="https://img.shields.io/github/v/release/codeclassroom/codeclassroom" target="_blank" />
</a>
<a href="https://requires.io/github/codeclassroom/codeclassroom/requirements/?branch=master">
<img alt="dependencies" src="https://requires.io/github/codeclassroom/codeclassroom/requirements.svg?branch=master" target="_blank" />
<a href="https://github.com/ellerbrock/open-source-badges">
<img alt="opensourcelove" src="https://badges.frapsoft.com/os/v1/open-source.png?v=103" target="_blank" />
</a>
<a href="https://www.codacy.com/gh/codeclassroom/codeclassroom?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=codeclassroom/codeclassroom&amp;utm_campaign=Badge_Grade">
<img alt="Codacy" src="https://api.codacy.com/project/badge/Grade/91c4c80af77442d5979eb5253afa3759" target="_blank" />
Expand Down Expand Up @@ -56,6 +56,7 @@ python3 manage.py test
python3 manage.py runserver
```


## Change log 📝

Changelog can be found in [CHANGELOG.md](https://github.com/codeclassroom/codeclassroom/blob/master/CHANGELOG.md).
Expand Down
45 changes: 31 additions & 14 deletions api/urls.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
from django.urls import path, include
from .views import (
index,
UserLoginView, UserLogoutView,
StudentSignupView, StudentViewSet, StudentDetail,
ProfessorSignupView, ProfessorViewSet, ProfessorDetail,
ClassroomCreateView, ClassroomJoinView, ClassroomList, ClassroomDetail,
AssignmentList, AssignmentCreate, AssignmentDetail,
QuestionList, QuestionCreate, QuestionDetail,
SubmissionCreate, SubmissionList, SubmissionDetail,
PlagiarismView, RunCode
from django.urls import path
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import permissions

from .views import (AssignmentCreate, AssignmentDetail, AssignmentList,
ClassroomCreateView, ClassroomDetail, ClassroomJoinView,
ClassroomList, FeedBackView, JudgeCode, PlagiarismReport,
PlagiarismView, ProfessorDetail, ProfessorSignupView,
ProfessorViewSet, QuestionCreate, QuestionDetail,
QuestionList, ReportQuesiton, StudentDetail,
StudentSignupView, StudentViewSet, SubmissionCreate,
SubmissionDetail, SubmissionList, UserLoginView,
UserLogoutView)

schema_view = get_schema_view(
openapi.Info(
title="CodeClassroom API",
default_version='v2.1.0',
description="A platform for managing programming assignments in colleges & universities.",
contact=openapi.Contact(email="varshneybhupes@gmail.com"),
license=openapi.License(name="AGPL-3.0"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)


Expand All @@ -20,7 +34,7 @@


urlpatterns = [
path('', index, name='index'),
path('', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('signup/professor', ProfessorSignupView.as_view(), name='prof-signup'),
path('signup/student', StudentSignupView.as_view(), name='student-signup'),
path('login/', UserLoginView.as_view(), name='login'),
Expand Down Expand Up @@ -54,8 +68,11 @@
path('submissions', SubmissionList.as_view(), name='submissions'),

# Utility URLs
path('judge', RunCode.as_view(), name='code-judge'),
path('judge/', JudgeCode.as_view(), name='code-judge'),
path('codesim/', PlagiarismView.as_view(),
name='plagiarism-detector')
name='plagiarism-detector'),
path('email/feedback', FeedBackView.as_view(), name='feedback'),
path('email/report', ReportQuesiton.as_view(), name='report-question'),
path('email/plagiarism-report', PlagiarismReport.as_view(), name='report-plagiarism')

]
146 changes: 109 additions & 37 deletions api/views.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
'''API views.'''
from django.contrib.auth import authenticate, login, logout
from rest_framework.permissions import AllowAny
from drf_yasg.utils import swagger_serializer_method
from rest_framework import generics, status, views, viewsets
from rest_framework.decorators import api_view, permission_classes
from rest_framework import views
from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework import generics
from rest_framework import viewsets
from rest_framework import status
from rest_framework.parsers import MultiPartParser, FormParser
from app.models import (Professor, Student, Classroom, Assignment, Question, Solution)
from app.serializers import (
UserLoginSerializer,
ProfessorSignupSerializer, ProfessorSerializer,
StudentSignupSerializer, StudentSerializer,
ClassroomCreateSerializer, ClassroomJoincodeSerializer, ClassroomSerializer,
AssignmentSerializer,
QuestionSerializer,
SolutionSerializer,
)
from utilities.judge import run_code, submit_code

from app.models import (Assignment, Classroom, Professor, Question, Solution,
Student)
from app.serializers import (AssignmentSerializer, ClassroomCreateSerializer,
ClassroomJoincodeSerializer, ClassroomSerializer,
FeedBackEmailSerializer, JudgeSerializer,
PlagiarismReportSerializer, PlagiarismSerializer,
ProfessorSerializer, ProfessorSignupSerializer,
QuestionSerializer, ReportQuestionSerializer,
SolutionSerializer, StudentSerializer,
StudentSignupSerializer, UserLoginSerializer)
from utilities.codesim import codesim
from utilities.email import feedback, plagiarism_report, report
from utilities.judge import run_code, submit_code


@api_view(['GET'])
Expand Down Expand Up @@ -124,6 +124,7 @@ class ClassroomCreateView(views.APIView):
'''API view for creating a classroom'''
serializer_class = ClassroomCreateSerializer

@swagger_serializer_method(serializer_or_field=ClassroomCreateSerializer)
def post(self, request):
serializer = ClassroomCreateSerializer(data=request.data)

Expand All @@ -133,10 +134,11 @@ def post(self, request):
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class ClassroomJoinView(views.APIView):
class ClassroomJoinView(generics.CreateAPIView):
'''API view for joining a classroom'''
serializer_class = ClassroomJoincodeSerializer

@swagger_serializer_method(serializer_or_field=ClassroomJoincodeSerializer)
def post(self, request):
serializer = ClassroomJoincodeSerializer(data=request.data)

Expand All @@ -163,10 +165,11 @@ class ClassroomDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Classroom.objects.all()


class AssignmentCreate(views.APIView):
class AssignmentCreate(generics.CreateAPIView):
'''API view for creating assignments'''
serializer_class = AssignmentSerializer

@swagger_serializer_method(serializer_or_field=AssignmentSerializer)
def post(self, request):
serializer = AssignmentSerializer(data=request.data)

Expand All @@ -188,10 +191,11 @@ class AssignmentDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Assignment.objects.all()


class QuestionCreate(views.APIView):
class QuestionCreate(generics.CreateAPIView):
'''API view for creating questions'''
serializer_class = QuestionSerializer

@swagger_serializer_method(serializer_or_field=QuestionSerializer)
def post(self, request):
serializer = QuestionSerializer(data=request.data)

Expand All @@ -213,15 +217,22 @@ class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()


class RunCode(views.APIView):
'''API for running code.'''
class JudgeCode(generics.CreateAPIView):
'''API for Judging Code'''
serializer_class = JudgeSerializer

@swagger_serializer_method(serializer_or_field=JudgeSerializer)
def post(self, request):
code = request.data["code"]
lang = request.data["language"]
question = request.data["question_id"]
serializer = JudgeSerializer(data=request.data)
if serializer.is_valid():
code = request.data["code"]
lang = request.data["language"]

content = run_code(code, lang, question)
if request.data["testcases"] != "":
content = run_code(code, lang, testcase=request.data["testcases"])
elif request.data["question_id"] != "":
question = request.data["question_id"]
content = run_code(code, lang, question)

return Response(content, status=status.HTTP_200_OK)

Expand All @@ -231,6 +242,7 @@ class SubmissionCreate(generics.CreateAPIView):
serializer_class = SolutionSerializer
parser_classes = (MultiPartParser, FormParser)

@swagger_serializer_method(serializer_or_field=SolutionSerializer)
def post(self, request):

serializer = SolutionSerializer(data=request.data)
Expand Down Expand Up @@ -270,16 +282,76 @@ class SubmissionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Solution.objects.all()


class PlagiarismView(views.APIView):
'''API View for running plagiarism service'''
class PlagiarismView(generics.CreateAPIView):
'''API View for running Plagiarism Service'''
serializer_class = PlagiarismSerializer

@swagger_serializer_method(serializer_or_field=PlagiarismSerializer)
def post(self, request):
serializer = PlagiarismSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
try:
Assignment.objects.get(pk=request.data["assignment"])
results = codesim(request.data["assignment"])
return Response(results, status=status.HTTP_200_OK)
except Assignment.DoesNotExist:
return Response(
{"detail": "Assignment Does Not Exist"},
status=status.HTTP_404_NOT_FOUND
)


class FeedBackView(generics.CreateAPIView):
"""Anonymous Feedback for CodeClassroom Website Creators"""
serializer_class = FeedBackEmailSerializer
permission_classes = [AllowAny]

@swagger_serializer_method(serializer_or_field=FeedBackEmailSerializer)
def post(self, request):
serializer = FeedBackEmailSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
email_status = feedback(serializer.data)
if email_status:
return Response(
{"detail": "Thanks for your feedback"},
status=status.HTTP_202_ACCEPTED
)
else:
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class ReportQuesiton(generics.CreateAPIView):
"""Report Professors for any error/feedback in Question by sending them an email"""
serializer_class = ReportQuestionSerializer

@swagger_serializer_method(serializer_or_field=ReportQuestionSerializer)
def post(self, request):
try:
Assignment.objects.get(pk=request.data["assignment"])
results = codesim(request.data["assignment"])
return Response(results, status=status.HTTP_200_OK)
except Assignment.DoesNotExist:
return Response(
{"detail": "Assignment Does Not Exist"},
status=status.HTTP_404_NOT_FOUND
)
serializer = ReportQuestionSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
report_status = report(serializer.data)
if report_status:
return Response(
{"detail": "Thanks for your feedback"},
status=status.HTTP_202_ACCEPTED
)
else:
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class PlagiarismReport(generics.CreateAPIView):
"""Report Students for plagiarism by sending them an email
Accepts User ID instead of Student ID"""
serializer_class = PlagiarismReportSerializer

@swagger_serializer_method(serializer_or_field=PlagiarismReportSerializer)
def post(self, request):
serializer = PlagiarismReportSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
report_status = plagiarism_report(serializer.data)
if report_status:
return Response(
{"detail": "Students have been notified"},
status=status.HTTP_202_ACCEPTED
)
else:
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Loading