Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Completed Newbie Project #292

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ frontend/yarn-error.log
__pycache__/
*.pyc

.env
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!


# Distribution
build/
dist/
Expand Down
3 changes: 2 additions & 1 deletion backend/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dj-database-url = "*"
djangorestframework = "*"
psycopg2 = "*"
sentry-sdk = "*"
django = "==3.1.7"
django = "==3.2.4"
django-cors-headers = "*"
pyyaml = "*"
uritemplate = "*"
Expand All @@ -43,6 +43,7 @@ gunicorn = "*"
django-scheduler = "*"
typing-extensions = "*"
drf-excel = "*"
langchain = "*"

[requires]
python_version = "3"
2,266 changes: 1,314 additions & 952 deletions backend/Pipfile.lock

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions backend/officehoursqueue/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@
TWILIO_AUTH_TOKEN = os.environ.get("TWILIO_TOKEN", "")
TWILIO_NUMBER = os.environ.get("TWILIO_NUMBER", "")

# Pinecone Settings

PINECONE_API_KEY=os.environ.get("PINECONE_API_KEY", "")

# OpenAI Settings

OPENAI_API_KEY=os.environ.get("OPENAI_API_KEY", "")

# Redis URL used for celery, channels and general caching.
REDIS_URL = os.environ.get("REDIS_URL", "redis://localhost")

Expand Down
4 changes: 4 additions & 0 deletions backend/ohq/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Announcement,
Course,
CourseStatistic,
Document,
Membership,
MembershipInvite,
Profile,
Expand All @@ -12,11 +13,13 @@
QueueStatistic,
Semester,
Tag,
VectorDB,
)


admin.site.register(Course)
admin.site.register(CourseStatistic)
admin.site.register(Document)
admin.site.register(Membership)
admin.site.register(MembershipInvite)
admin.site.register(Profile)
Expand All @@ -26,3 +29,4 @@
admin.site.register(QueueStatistic)
admin.site.register(Announcement)
admin.site.register(Tag)
admin.site.register(VectorDB)
40 changes: 40 additions & 0 deletions backend/ohq/migrations/0020_auto_20231008_1655.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Generated by Django 3.2.4 on 2023-10-08 16:55

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('ohq', '0019_auto_20211114_1800'),
]

operations = [
migrations.CreateModel(
name='VectorDB',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('time_updated', models.DateTimeField(auto_now=True)),
('top_k', models.IntegerField(blank=True, null=True)),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ohq.course')),
],
),
migrations.CreateModel(
name='Document',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('vector_db', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ohq.vectordb')),
],
),
migrations.AddConstraint(
model_name='vectordb',
constraint=models.UniqueConstraint(fields=('name', 'course'), name='unique_VectorDB'),
),
migrations.AddConstraint(
model_name='document',
constraint=models.UniqueConstraint(fields=('name',), name='unique_document'),
),
]
24 changes: 24 additions & 0 deletions backend/ohq/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,3 +411,27 @@ class Announcement(models.Model):
author = models.ForeignKey(User, related_name="announcements", on_delete=models.CASCADE)
time_updated = models.DateTimeField(auto_now=True)
course = models.ForeignKey(Course, related_name="announcements", on_delete=models.CASCADE)


class VectorDB(models.Model):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also add a __str__ method to this model to format the name of the VectorDB objects within django admin. So perhaps:

def __str__(self):
      return f"{self.course}: {self.name} Vector DB"


name = models.CharField(max_length=255)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
time_updated = models.DateTimeField(auto_now=True)
top_k = models.IntegerField(blank=True, null=True)

class Meta:
constraints = [models.UniqueConstraint(fields=["name", "course"], name="unique_VectorDB")]

def __str__(self):
return f"{self.course}: {self.name} Vector DB"

class Document(models.Model):
name = models.CharField(max_length=255)
vector_db = models.ForeignKey(VectorDB, on_delete=models.CASCADE)

class Meta:
constraints = [models.UniqueConstraint(fields=["name"], name="unique_document")]

def __str__(self):
return f"{self.course}: {self.name}"
104 changes: 104 additions & 0 deletions backend/ohq/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,3 +503,107 @@ def has_permission(self, request, view):
return True

return True

class DocumentCreatePermission(permissions.BasePermission):
def has_permission(self, request, view):
if not request.user.is_authenticated:
return False

membership = Membership.objects.filter(
course=view.kwargs["course_pk"], user=request.user
).first()

# Non-Students can't do anything
if membership is None:
return False

if view.action == "retrieve":
return membership.is_ta or membership.is_leadership

# Head TAs+ can make changes
if view.action in ["create", "destroy", "update", "partial_update"]:
return membership.is_leadership

class DocumentPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):

membership = Membership.objects.get(course=view.kwargs["course_pk"], user=request.user)

if view.action == "retrieve":
return membership.is_ta or membership.is_leadership

if view.action in ["create", "destroy", "partial_update", "update"]:
return membership.is_leadership

return False

def has_permission(self, request, view):
if not request.user.is_authenticated:
return False

membership = Membership.objects.filter(
course=view.kwargs["course_pk"], user=request.user
).first()

# Non-Students can't do anything
if membership is None:
return False

if view.action == "retrieve":
trangiabach marked this conversation as resolved.
Show resolved Hide resolved
return membership.is_ta or membership.is_leadership

# Head TAs+ can make changes
if view.action in ["create", "destroy", "update", "partial_update"]:
return membership.is_leadership

class VectorSearchPermission(permissions.BasePermission):
def has_permission(self, request, view):
if not request.user.is_authenticated:
return False

membership = Membership.objects.filter(
course=view.kwargs["course_pk"], user=request.user
).first()

# Non-Students can't do anything
if membership is None:
return False

if view.action == "retrieve":
trangiabach marked this conversation as resolved.
Show resolved Hide resolved
return membership.is_ta or membership.is_leadership

# Head TAs+ can make changes
if view.action in ["create", "destroy", "update", "partial_update"]:
return membership.is_leadership

class VectorDBPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):

membership = Membership.objects.get(course=view.kwargs["course_pk"], user=request.user)

if view.action == "retrieve":
return membership.is_ta or membership.is_leadership

if view.action in ["create", "destroy", "partial_update", "update"]:
return membership.is_leadership

return False

def has_permission(self, request, view):
if not request.user.is_authenticated:
return False

membership = Membership.objects.filter(
course=view.kwargs["course_pk"], user=request.user
).first()

# Non-Students can't do anything
if membership is None:
return False

if view.action == "retrieve":
return membership.is_ta or membership.is_leadership

# Head TAs+ can make changes
if view.action in ["create", "destroy", "update", "partial_update"]:
return membership.is_leadership
12 changes: 12 additions & 0 deletions backend/ohq/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Announcement,
Course,
CourseStatistic,
Document,
Membership,
MembershipInvite,
Profile,
Expand All @@ -22,6 +23,7 @@
QueueStatistic,
Semester,
Tag,
VectorDB,
)
from ohq.sms import sendSMSVerification
from ohq.tasks import sendUpNextNotificationTask
Expand Down Expand Up @@ -574,3 +576,13 @@ class OccurrenceSerializer(serializers.ModelSerializer):
class Meta:
model = Occurrence
fields = ("id", "title", "description", "start", "end", "cancelled", "event")

class DocumentSerializer(serializers.ModelSerializer):
class Meta:
model = Document
fields = ("id", "name", "vector_db")

class VectorDBSerializer(serializers.ModelSerializer):
class Meta:
model = VectorDB
fields = ("id", "name", "course", "time_updated", "top_k")
20 changes: 19 additions & 1 deletion backend/ohq/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
AnnouncementViewSet,
CourseStatisticView,
CourseViewSet,
DocumentViewSet,
DocumentCreateView,
EventViewSet,
MassInviteView,
MembershipInviteViewSet,
Expand All @@ -19,6 +21,8 @@
SemesterViewSet,
TagViewSet,
UserView,
VectorDBViewSet,
VectorSearchView,
)


Expand All @@ -36,10 +40,14 @@
course_router.register("invites", MembershipInviteViewSet, basename="invite")
course_router.register("announcements", AnnouncementViewSet, basename="announcement")
course_router.register("tags", TagViewSet, basename="tag")
course_router.register("vector_dbs", VectorDBViewSet, basename="vector_dbs")

queue_router = routers.NestedSimpleRouter(course_router, "queues", lookup="queue")
queue_router.register("questions", QuestionViewSet, basename="question")

vector_db_router = routers.NestedSimpleRouter(course_router, "vector_dbs", lookup="vector_dbs")
vector_db_router.register("documents", DocumentViewSet, basename="documents")

realtime_router = RealtimeRouter()
realtime_router.register(QuestionViewSet)
realtime_router.register(AnnouncementViewSet)
Expand All @@ -61,6 +69,16 @@
CourseStatisticView.as_view(),
name="course-statistic",
),
path(
"courses/<slug:course_pk>/vector_dbs/create",
DocumentCreateView.as_view(),
name="vectordb-create-documents",
),
path(
"courses/<slug:course_pk>/vector_dbs/search",
VectorSearchView.as_view(),
name="document-search",
)
]

urlpatterns = router.urls + course_router.urls + queue_router.urls + additional_urls
urlpatterns = router.urls + course_router.urls + queue_router.urls + vector_db_router.urls + additional_urls
Loading
Loading