Skip to content
Open
Show file tree
Hide file tree
Changes from 14 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: 3 additions & 2 deletions zapisy/apps/enrollment/courses/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from apps.enrollment.courses.models.course_type import Type
from apps.enrollment.courses.models.group import Group, GuaranteedSpots
from apps.enrollment.courses.models.semester import ChangedDay, Freeday, Semester
from apps.enrollment.courses.models.tag import Tag
from apps.enrollment.courses.models.tags import SpecialistTag, ThematicTag
from apps.enrollment.courses.models.term import Term
from apps.enrollment.records.models import GroupOpeningTimes, Record, RecordStatus, T0Times

Expand Down Expand Up @@ -155,7 +155,8 @@ class TypeAdmin(admin.ModelAdmin):


admin.site.register(Group, GroupAdmin)
admin.site.register(Tag)
admin.site.register(ThematicTag)
admin.site.register(SpecialistTag)
admin.site.register(Classroom, ClassroomAdmin)
admin.site.register(Semester, SemesterAdmin)
admin.site.register(Freeday, FreedayAdmin)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Generated by Django 3.1.14 on 2025-01-05 14:57

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


class Migration(migrations.Migration):
Expand All @@ -17,4 +17,35 @@ class Migration(migrations.Migration):
migrations.DeleteModel(
name='Effects',
),
migrations.CreateModel(
name='SpecialistTag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('short_name', models.CharField(max_length=50, verbose_name='nazwa skrócona')),
('full_name', models.CharField(max_length=250, verbose_name='nazwa pełna')),
('description', models.TextField(verbose_name='opis')),
],
options={
'verbose_name': 'Tag specjalistyczny (I st.)',
'verbose_name_plural': 'Tagi specjalistyczne (I st.)',
},
),
migrations.RenameModel(
old_name='Tag',
new_name='ThematicTag',
),
migrations.AlterModelOptions(
name='thematictag',
options={'verbose_name': 'Tag specjalistyczny (II st.)', 'verbose_name_plural': 'Tagi specjalistyczne (II st.)'},
),
Comment on lines +37 to +40
Copy link
Contributor

Choose a reason for hiding this comment

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

To chyba odnosi się do zmiany opcji starego modelu tag, ale wartości tekstowe nie odpowiadają jego nowej nazwie thematictag. Być może to po prostu jakaś stara wersja migracji, ale trzeba coś z tym zrobić.

migrations.AddField(
model_name='courseinformation',
name='specialist_tags',
field=models.ManyToManyField(blank=True, to='courses.SpecialistTag', verbose_name='Tagi specjalistyczne (II st.)'),
),
migrations.RenameField(
model_name='courseinformation',
old_name='tags',
new_name='thematic_tags',
),
]
16 changes: 10 additions & 6 deletions zapisy/apps/enrollment/courses/models/course_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from apps.users.models import Employee

from .course_type import Type as CourseType
from .tag import Tag
from .tags import SpecialistTag, ThematicTag


class Language(models.TextChoices):
Expand Down Expand Up @@ -71,7 +71,8 @@ class CourseInformation(models.Model):
year = models.CharField("rok studiów", max_length=50, blank=True)
discipline = models.CharField("dyscyplina", max_length=100, default="Informatyka")

tags = models.ManyToManyField(Tag, verbose_name="tagi", blank=True)
thematic_tags = models.ManyToManyField(ThematicTag, verbose_name="Tagi tematyczne (I st.)", blank=True)
specialist_tags = models.ManyToManyField(SpecialistTag, verbose_name="Tagi specjalistyczne (II st.)", blank=True)

created = models.DateTimeField("Data utworzenia", auto_now_add=True)
modified = models.DateTimeField("Data modyfikacji", auto_now=True)
Expand Down Expand Up @@ -111,14 +112,15 @@ def __copy__(self) -> 'CourseInformation':
return copy

def __json__(self):
"""Returns a JSON-serializable dict with al course information."""
"""Returns a JSON-serializable dict with all course information."""
return {
'id': self.id,
'name': self.name,
'courseType': self.course_type_id,
'recommendedForFirstYear': self.recommended_for_first_year,
'owner': self.owner_id,
'tags': [tag.pk for tag in self.tags.all()],
'thematic_tags': [tag.pk for tag in self.thematic_tags.all()],
'specialist_tags': [tag.pk for tag in self.specialist_tags.all()],
}

def get_short_name(self):
Expand All @@ -127,12 +129,14 @@ def get_short_name(self):
@staticmethod
def prepare_filter_data(qs: models.QuerySet) -> Dict:
"""Prepares the data for course filter based on a given queryset."""
all_tags = Tag.objects.all().values_list('id', 'full_name', named=True)
all_thematic_tags = ThematicTag.objects.all().values_list('id', 'full_name', named=True)
all_specialist_tags = SpecialistTag.objects.all().values_list('id', 'full_name', named=True)
all_owners = qs.values_list(
'owner', 'owner__user__first_name', 'owner__user__last_name', named=True).distinct()
all_types = qs.values_list('course_type', 'course_type__name', named=True).distinct()
return {
'allTags': {t.id: t.full_name for t in all_tags},
'allThematicTags': {t.id: t.full_name for t in all_thematic_tags},
'allSpecialistTags': {t.id: t.full_name for t in all_specialist_tags},
'allOwners': {
o.owner: [o.owner__user__first_name, o.owner__user__last_name] for o in all_owners
},
Expand Down
3 changes: 2 additions & 1 deletion zapisy/apps/enrollment/courses/models/course_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ def create_proposal_instance(cls, proposal: Proposal, semester: Semester):

instance = cls(**proposal_dict)
instance.save()
instance.tags.set(proposal.tags.all())
instance.thematic_tags.set(proposal.thematic_tags.all())
instance.specialist_tags.set(proposal.specialist_tags.all())
return instance

@classmethod
Expand Down
23 changes: 0 additions & 23 deletions zapisy/apps/enrollment/courses/models/tag.py

This file was deleted.

35 changes: 35 additions & 0 deletions zapisy/apps/enrollment/courses/models/tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django.db import models


class BaseTag(models.Model):
short_name = models.CharField(max_length=50, verbose_name='nazwa skrócona')
full_name = models.CharField(max_length=250, verbose_name='nazwa pełna')
description = models.TextField(verbose_name='opis')

class Meta:
abstract = True

def __str__(self):
return f"{self.short_name} ({self.full_name})"

def serialize_for_json(self):
return {
'id': self.pk,
'short_name': self.short_name,
'full_name': self.full_name,
'description': self.description,
}


class ThematicTag(BaseTag):
class Meta:
verbose_name = 'Tag tematyczny (I st.)'
verbose_name_plural = 'Tagi tematyczne (I st.)'
app_label = 'courses'


class SpecialistTag(BaseTag):
class Meta:
verbose_name = 'Tag specjalistyczny (II st.)'
verbose_name_plural = 'Tagi specjalistyczne (II st.)'
Comment on lines +26 to +34
Copy link
Contributor

Choose a reason for hiding this comment

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

To może rewolucyjne, ale przy okazji sprawmy, żeby w systemie posługiwać się językiem polskim (i jednocześnie stosować nomenklaturę pojawiającą się w programie studiów) – plus oczywiście analogiczne zmiany w innych plikach.

Suggested change
verbose_name = 'Tag tematyczny (I st.)'
verbose_name_plural = 'Tagi tematyczne (I st.)'
app_label = 'courses'
class SpecialistTag(BaseTag):
class Meta:
verbose_name = 'Tag specjalistyczny (II st.)'
verbose_name_plural = 'Tagi specjalistyczne (II st.)'
verbose_name = "Znacznik tematyczny (I st.)"
verbose_name_plural = "Znaczniki tematyczne (I st.)"
app_label = 'courses'
class SpecialistTag(BaseTag):
class Meta:
verbose_name = "Znacznik specjalistyczny (II st.)"
verbose_name_plural = "Znaczniki specjalistyczne (II st.)"

(Podwójne cudzysłowy stosujemy dla napisów przeznaczonych "dla ludzkich oczu", a pojedyncze – dla tych przetwarzanych wewnętrznie w kodzie.)

app_label = 'courses'
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,30 @@ <h1 class="d-inline-block">
<th scope="row">Egzamin</th>
<td>{{ course.has_exam|yesno|capfirst }}</td>
</tr>
{% if course.tags.all %}
{% if course.thematic_tags.all %}
<tr>
<th scope="row">Tagi</th>
<th scope="row">Tagi tematyczne</th>
<td class="badges">
<ul>
{% for tag in course.tags.all %}
{% for tag in course.thematic_tags.all %}
<li class="badge bg-success me-2">{{ tag }}</li>
{% endfor %}
</ul>
</td>
</tr>
{% endif %}
{% if course.specialist_tags.all %}
<tr>
<th>Tagi specjalistyczne</th>
<td class="badges">
<ul>
{% for tag in course.specialist_tags.all %}
<li class="badge bg-info me-2">{{ tag }}</li>
{% endfor %}
</ul>
</td>
</tr>
{% endif %}
</tbody>
</table>

Expand Down
4 changes: 2 additions & 2 deletions zapisy/apps/enrollment/courses/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def prepare_courses_list_data(semester: Optional[Semester]):
"""Returns a dict used by course list and filter in various views."""
qs = CourseInstance.objects.filter(semester=semester).order_by('name')
courses = []
for course in qs.prefetch_related('tags'):
for course in qs.prefetch_related('thematic_tags', 'specialist_tags'):
course_dict = course.__json__()
course_dict.update({
'url': reverse('course-page', args=(course.slug,)),
Expand Down Expand Up @@ -68,7 +68,7 @@ def course_view_data(request, slug) -> Tuple[Optional[CourseInstance], Optional[
course: CourseInstance
try:
course = CourseInstance.objects.filter(slug=slug).select_related(
'semester', 'course_type').prefetch_related('tags').get()
'semester', 'course_type').prefetch_related('thematic_tags', 'specialist_tags').get()
except CourseInstance.DoesNotExist:
return None, None

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export default Vue.extend({
},
data: function () {
return {
allTags: {},
allThematicTags: {},
allSpecialistTags: {},
allOwners: [] as MultiselectFilterData<number>,
allTypes: [] as MultiselectFilterData<number>,
// The filters are going to be collapsed by default.
Expand All @@ -30,7 +31,8 @@ export default Vue.extend({
const filtersData = JSON.parse(
document.getElementById("filters-data")!.innerHTML
) as FilterDataJSON;
this.allTags = cloneDeep(filtersData.allTags);
this.allThematicTags = cloneDeep(filtersData.allThematicTags || {});
this.allSpecialistTags = cloneDeep(filtersData.allSpecialistTags || {});
Comment on lines +34 to +35
Copy link
Contributor

Choose a reason for hiding this comment

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

Skąd || {}? Dlaczego w innych CourseFilter.vue tego nie dodajemy? (To uczciwe pytanie, nie sugestia, że to źle…) Dlaczego w innych CourseFilter.vue tego nie dodajemy?

this.allOwners = toPairs(filtersData.allOwners)
.sort(([id, [firstname, lastname]], [id2, [firstname2, lastname2]]) => {
const lastNamesComparison = lastname.localeCompare(lastname2, "pl");
Expand Down Expand Up @@ -78,12 +80,12 @@ export default Vue.extend({
/>
<hr />
<LabelsFilter
title="Tagi"
filterKey="tags-filter"
property="tags"
:allLabels="allTags"
title="Tagi tematyczne (I st.)"
filterKey="thematic-tags-filter"
property="thematic_tags"
:allLabels="allThematicTags"
onClass="bg-success"
ref="tags-filter"
ref="thematic-tags-filter"
/>
</div>
<div class="col-md">
Expand All @@ -95,6 +97,17 @@ export default Vue.extend({
placeholder="Wszystkie rodzaje"
ref="type-filter"
/>
<hr />
<LabelsFilter
title="Tagi specjalistyczne (II st.)"
filterKey="specialist-tags-filter"
property="specialist_tags"
:allLabels="allSpecialistTags"
onClass="bg-info"
ref="specialist-tags-filter"
/>
</div>
<div class="col-md">
<MultiSelectFilter
filterKey="owner-filter"
property="owner"
Expand Down
3 changes: 2 additions & 1 deletion zapisy/apps/enrollment/timetable/assets/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ export interface PersonDict {
}

export interface FilterDataJSON {
allTags: KVDict;
allThematicTags: KVDict;
allSpecialistTags: KVDict;
allOwners: PersonDict;
allTypes: KVDict;
}
Expand Down
3 changes: 2 additions & 1 deletion zapisy/apps/enrollment/timetable/assets/store/courses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export interface CourseInfo {
id: number;
name: string;
url: string;
tags: Array<number>;
thematicTags: Array<number>;
specialistTags: Array<number>;
owner: number;
recommendedForFirstYear: boolean;

Expand Down
2 changes: 1 addition & 1 deletion zapisy/apps/enrollment/timetable/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def list_courses_in_semester(semester: Semester):

This list will be used in prototype.
"""
qs = CourseInstance.objects.filter(semester=semester).prefetch_related('tags')
qs = CourseInstance.objects.filter(semester=semester).prefetch_related('thematic_tags', 'specialist_tags')
courses = []
for course in qs:
course_dict = course.__json__()
Expand Down
19 changes: 11 additions & 8 deletions zapisy/apps/offer/assignments/sheets.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ def read_assignments_sheet(sheet: gspread.models.Spreadsheet) -> Iterator[Single

def update_courses_sheet(sheet: gspread.models.Spreadsheet, courses: List[SingleCourseData]):
data = [[
'Proposal ID', 'Przedmiot', 'Rodzaj', 'Tagi', 'ECTS', 'Semestr',
'Proposal ID', 'Przedmiot', 'Rodzaj', 'Tagi tematyczne (I st.)',
'Tagi specjalistyczne (II st.)', 'ECTS', 'Semestr',
'Planowana liczba grup', 'Uruchomiona liczba grup'
]]

Expand All @@ -294,17 +295,18 @@ def update_courses_sheet(sheet: gspread.models.Spreadsheet, courses: List[Single
group.proposal_id, # A. proposal_id
group.name, # B. course name
group.course_type, # C. course type
group.tags, # D. tags
group.ects, # E. ECTS
group.semester, # F. semester
f'=COUNTIFS(Przydziały!A2:A; A{i}; Przydziały!I2:I; F{i})', # G. planned groups
f'=COUNTIFS(Przydziały!A2:A; A{i}; Przydziały!I2:I; F{i}; Przydziały!K2:K; True)', # H. active groups
group.thematic_tags, # D. thematic tags
group.specialist_tags, # E. specialist tags
group.ects, # F. ECTS
group.semester, # G. semester
f'=COUNTIFS(Przydziały!A2:A; A{i}; Przydziały!I2:I; F{i})', # H. planned groups
f'=COUNTIFS(Przydziały!A2:A; A{i}; Przydziały!I2:I; F{i}; Przydziały!K2:K; True)', # I. active groups
]
data.append(row)

worksheet = find_or_insert_worksheet(sheet, "Przedmioty")
worksheet.clear()
worksheet.update('A:H', data, raw=False)
worksheet.update('A:I', data, raw=False)
worksheet.freeze(rows=1)


Expand All @@ -323,7 +325,8 @@ def read_courses_sheet(sheet: gspread.models.Spreadsheet) -> Iterator[SingleCour
proposal_id=int(row['Proposal ID']),
name=row['Przedmiot'],
course_type=row['Rodzaj'],
tags=row['Tagi'],
thematic_tags=row['Tagi tematyczne (I st.)'],
specialist_tags=row['Tagi specjalistyczne (II st.)'],
ects=row['ECTS'],
semester=row['Semestr']
)
Expand Down
3 changes: 2 additions & 1 deletion zapisy/apps/offer/assignments/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ class SingleCourseData(NamedTuple):
name: str
proposal_id: int
course_type: str
tags: str
thematic_tags: str
specialist_tags: str
ects: str
semester: str

Expand Down
5 changes: 3 additions & 2 deletions zapisy/apps/offer/assignments/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def create_assignments_sheet(request):
p.id: p for p in Proposal.objects
.filter(id__in=proposal_ids)
.select_related('owner', 'owner__user', 'course_type')
.prefetch_related('tags')
.prefetch_related('thematic_tags', 'specialist_tags')
}

# update Assignments sheet
Expand Down Expand Up @@ -243,7 +243,8 @@ def create_assignments_sheet(request):
proposal_id=pid,
name=proposal.get_course_name(semester),
course_type=proposal.course_type.name,
tags=', '.join(map(lambda x: x[0], proposal.tags.values_list('short_name'))),
thematic_tags=', '.join(map(lambda x: x[0], proposal.thematic_tags.values_list('short_name'))),
specialist_tags=', '.join(map(lambda x: x[0], proposal.specialist_tags.values_list('short_name'))),
ects=proposal.points,
semester=semester
)
Expand Down
Loading
Loading