├── zapisy ├── apps │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── rest │ │ │ ├── __init__.py │ │ │ └── v1 │ │ │ ├── __init__.py │ │ │ ├── tests │ │ │ └── __init__.py │ │ │ └── api_wrapper │ │ │ ├── sz_api │ │ │ └── __init__.py │ │ │ ├── README.md │ │ │ └── setup.py │ ├── news │ │ ├── __init__.py │ │ ├── tests │ │ │ ├── __init__.py │ │ │ └── utils.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ ├── 0003_markdown.py │ │ │ └── 0002_auto_20180525_0559.py │ │ ├── urls.py │ │ └── admin.py │ ├── common │ │ ├── __init__.py │ │ ├── assets │ │ │ ├── tsfix │ │ │ │ ├── index.ts │ │ │ │ └── README.txt │ │ │ ├── main │ │ │ │ ├── types.ts │ │ │ │ ├── expose_libs.ts │ │ │ │ ├── sidebar-fold.js │ │ │ │ └── _variables.scss │ │ │ ├── markdown │ │ │ │ ├── render-markdown.scss │ │ │ │ ├── markdown-editor.js │ │ │ │ └── render-markdown.ts │ │ │ └── cookieconsent │ │ │ │ └── display-cookieconsent.ts │ │ ├── templatetags │ │ │ └── __init__.py │ │ ├── static │ │ │ └── common │ │ │ │ └── images │ │ │ │ ├── favicon.ico │ │ │ │ └── data-consent │ │ │ │ └── ecommerce-user-profile.png │ │ ├── redis.py │ │ ├── templates │ │ │ └── widgets │ │ │ │ └── markdown-editor.html │ │ ├── days_of_week.py │ │ └── widgets.py │ ├── effects │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ └── 0001_initial.py │ │ ├── apps.py │ │ ├── templates │ │ │ └── effects.html │ │ └── models.py │ ├── grade │ │ ├── __init__.py │ │ ├── poll │ │ │ ├── tests │ │ │ │ └── __init__.py │ │ │ ├── migrations │ │ │ │ ├── __init__.py │ │ │ │ ├── 0004_auto_20170606_1842.py │ │ │ │ ├── 0007_auto_20190529_1419.py │ │ │ │ ├── 0009_auto_20201029_1344.py │ │ │ │ └── 0006_auto_20190804_1129.py │ │ │ ├── __init__.py │ │ │ ├── assets │ │ │ │ └── bokeh.js │ │ │ ├── templates │ │ │ │ └── poll │ │ │ │ │ ├── _user_is_authenticated.html │ │ │ │ │ └── tickets_enter.html │ │ │ ├── apps.py │ │ │ ├── admin.py │ │ │ └── urls.py │ │ ├── ticket_create │ │ │ ├── tests │ │ │ │ └── __init__.py │ │ │ ├── migrations │ │ │ │ └── __init__.py │ │ │ ├── __init__.py │ │ │ ├── models │ │ │ │ ├── __init__.py │ │ │ │ └── student_graded.py │ │ │ ├── assets │ │ │ │ ├── katex.css │ │ │ │ ├── ticketsgenerate_main.js │ │ │ │ └── katex.ts │ │ │ ├── apps.py │ │ │ ├── urls.py │ │ │ └── signals.py │ │ └── urls.py │ ├── offer │ │ ├── __init__.py │ │ ├── proposal │ │ │ ├── __init__.py │ │ │ ├── tests │ │ │ │ ├── __init__.py │ │ │ │ └── factories.py │ │ │ ├── migrations │ │ │ │ ├── __init__.py │ │ │ │ ├── 0007_proposal_semester.py │ │ │ │ ├── 0003_auto_20170606_1842.py │ │ │ │ ├── 0008_auto_20190528_1403.py │ │ │ │ ├── 0006_proposal_semester.py │ │ │ │ └── 0002_auto_20170529_1617.py │ │ │ ├── templatetags │ │ │ │ ├── __init__.py │ │ │ │ └── proposal_status.py │ │ │ ├── templates │ │ │ │ └── proposal │ │ │ │ │ ├── edit-proposal.html │ │ │ │ │ ├── fields │ │ │ │ │ ├── custom_checkbox.html │ │ │ │ │ └── collapsable-fieldset.html │ │ │ │ │ └── proposal.html │ │ │ ├── assets │ │ │ │ └── course-list-index.js │ │ │ └── urls.py │ │ ├── vote │ │ │ ├── __init__.py │ │ │ ├── tests │ │ │ │ └── __init__.py │ │ │ ├── migrations │ │ │ │ ├── __init__.py │ │ │ │ ├── 0005_remove_singlevote_entity.py │ │ │ │ ├── 0006_auto_20190812_1025.py │ │ │ │ └── 0004_data_migration.py │ │ │ ├── templatetags │ │ │ │ ├── __init__.py │ │ │ │ └── vote_filters.py │ │ │ ├── models │ │ │ │ └── __init__.py │ │ │ ├── admin.py │ │ │ ├── urls.py │ │ │ └── assets │ │ │ │ ├── sortable-table.js │ │ │ │ ├── sortable-table.scss │ │ │ │ └── components │ │ │ │ └── CounterComponent.vue │ │ ├── desiderata │ │ │ ├── __init__.py │ │ │ ├── migrations │ │ │ │ ├── __init__.py │ │ │ │ ├── 0004_auto_20171216_2350.py │ │ │ │ └── 0003_auto_20171112_2306.py │ │ │ ├── urls.py │ │ │ ├── admin.py │ │ │ └── assets │ │ │ │ └── checkboxes-toggling.js │ │ ├── preferences │ │ │ ├── __init__.py │ │ │ ├── migrations │ │ │ │ ├── __init__.py │ │ │ │ ├── 0004_remove_preference_hidden.py │ │ │ │ ├── 0007_auto_20201106_2039.py │ │ │ │ ├── 0006_auto_20190804_1129.py │ │ │ │ └── 0002_auto_20170529_1617.py │ │ │ ├── urls.py │ │ │ ├── views.py │ │ │ ├── admin.py │ │ │ └── forms.py │ │ ├── urls.py │ │ └── assignments │ │ │ └── urls.py │ ├── schedule │ │ ├── __init__.py │ │ ├── tests │ │ │ └── __init__.py │ │ ├── management │ │ │ ├── __init__.py │ │ │ └── commands │ │ │ │ └── __init__.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ ├── 0009_term_ignore_conflicts.py │ │ │ ├── 0003_auto_20170601_1122.py │ │ │ ├── 0004_auto_20170606_1842.py │ │ │ ├── 0005_auto_20171112_2306.py │ │ │ ├── 0010_auto_20220801_2026.py │ │ │ └── 0008_auto_20191119_0017.py │ │ ├── models │ │ │ └── __init__.py │ │ ├── templatetags │ │ │ ├── __init__.py │ │ │ └── schedule_filters.py │ │ ├── static │ │ │ └── schedule │ │ │ │ └── feed-icon.png │ │ ├── assets │ │ │ ├── fullcalendar.scss │ │ │ ├── report.css │ │ │ ├── reservation-widget.js │ │ │ └── report.js │ │ └── templates │ │ │ ├── schedule │ │ │ ├── emails │ │ │ │ ├── new_exam.txt │ │ │ │ └── new_exam.html │ │ │ ├── reports │ │ │ │ └── base.html │ │ │ ├── history.html │ │ │ └── includes │ │ │ │ └── _report_form_render.html │ │ │ └── forms │ │ │ └── custom_visible_checkbox.html │ ├── enrollment │ │ ├── __init__.py │ │ ├── courses │ │ │ ├── __init__.py │ │ │ ├── tests │ │ │ │ ├── __init__.py │ │ │ │ └── semester_year_provider.py │ │ │ ├── migrations │ │ │ │ ├── __init__.py │ │ │ │ ├── 0016_merge_20190313_1524.py │ │ │ │ ├── 0015_remove_course_teachers.py │ │ │ │ ├── 0029_remove_term_classroom.py │ │ │ │ ├── 0004_auto_20171019_1034.py │ │ │ │ ├── 0018_remove_courseinformation_semester.py │ │ │ │ ├── 0026_courseinformation_discipline.py │ │ │ │ ├── 0031_auto_20200529_1126.py │ │ │ │ ├── 0008_auto_20180525_2259.py │ │ │ │ ├── 0025_type_obligatory.py │ │ │ │ ├── 0020_auto_20190721_1315.py │ │ │ │ ├── 0036_auto_20211022_1641.py │ │ │ │ ├── 0033_auto_20200914_1151.py │ │ │ │ ├── 0010_auto_20180804_2031.py │ │ │ │ ├── 0035_auto_20210130_0042.py │ │ │ │ ├── 0009_auto_20180712_1227.py │ │ │ │ ├── 0003_auto_20170606_1842.py │ │ │ │ ├── 0006_auto_20171216_1754.py │ │ │ │ ├── 0005_auto_20171112_2306.py │ │ │ │ └── 0015_auto_20190312_1330.py │ │ │ ├── templatetags │ │ │ │ └── __init__.py │ │ │ ├── models │ │ │ │ ├── __init__.py │ │ │ │ ├── effects.py │ │ │ │ └── tag.py │ │ │ ├── templates │ │ │ │ └── courses │ │ │ │ │ ├── course_parts │ │ │ │ │ ├── course_info.html │ │ │ │ │ └── course_consultations.html │ │ │ │ │ └── courses.html │ │ │ ├── assets │ │ │ │ └── course-list-index.js │ │ │ └── fixtures │ │ │ │ ├── users.yaml │ │ │ │ └── courses.yaml │ │ ├── records │ │ │ ├── tests │ │ │ │ ├── __init__.py │ │ │ │ └── factories.py │ │ │ ├── management │ │ │ │ ├── __init__.py │ │ │ │ └── commands │ │ │ │ │ └── __init__.py │ │ │ ├── migrations │ │ │ │ ├── __init__.py │ │ │ │ ├── 0011_auto_20201106_2029.py │ │ │ │ ├── 0007_record_priority.py │ │ │ │ ├── 0009_auto_20190109_1236.py │ │ │ │ ├── 0010_auto_20190822_1718.py │ │ │ │ ├── 0008_programgrouprestrictions.py │ │ │ │ └── 0006_auto_20180813_2243.py │ │ │ ├── __init__.py │ │ │ ├── models │ │ │ │ └── __init__.py │ │ │ ├── urls.py │ │ │ ├── apps.py │ │ │ └── signals.py │ │ ├── timetable │ │ │ ├── migrations │ │ │ │ ├── __init__.py │ │ │ │ ├── 0004_auto_20190822_1346.py │ │ │ │ ├── 0001_initial.py │ │ │ │ └── 0003_auto_20190215_0909.py │ │ │ ├── assets │ │ │ │ ├── components │ │ │ │ │ ├── PrototypeTimetable.vue │ │ │ │ │ └── PrototypeDay.vue │ │ │ │ ├── store │ │ │ │ │ └── index.ts │ │ │ │ ├── prototype-legend.scss │ │ │ │ └── prototype-index.js │ │ │ └── urls.py │ │ └── utils.py │ ├── schedulersync │ │ ├── __init__.py │ │ ├── tests │ │ │ └── __init__.py │ │ ├── management │ │ │ ├── __init__.py │ │ │ └── commands │ │ │ │ └── __init__.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ ├── 0002_auto_20180201_2236.py │ │ │ ├── 0004_delete_redundant_tables.py │ │ │ └── 0001_initial.py │ │ └── admin.py │ ├── statistics │ │ ├── __init__.py │ │ ├── urls.py │ │ ├── assets │ │ │ ├── store │ │ │ │ ├── index.ts │ │ │ │ └── filters.ts │ │ │ └── statistics-widget.js │ │ └── templates │ │ │ └── statistics │ │ │ ├── groups_list.html │ │ │ └── base.html │ ├── theses │ │ ├── tests │ │ │ └── __init__.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ ├── 0005_auto_20201106_2039.py │ │ │ └── 0003_create_theses_board_group.py │ │ ├── __init__.py │ │ ├── apps.py │ │ ├── assets │ │ │ ├── store │ │ │ │ ├── index.ts │ │ │ │ └── filters.ts │ │ │ └── theses-widget.js │ │ ├── urls.py │ │ ├── signals.py │ │ ├── system_settings.py │ │ ├── templates │ │ │ └── theses │ │ │ │ └── list_all.html │ │ ├── enums.py │ │ └── users.py │ ├── users │ │ ├── tests │ │ │ └── __init__.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ ├── 0015_merge_20190321_1311.py │ │ │ ├── 0016_remove_program_type_of_points.py │ │ │ ├── 0008_auto_20180601_1918.py │ │ │ ├── 0005_auto_20171019_1044.py │ │ │ ├── 0019_remove_email_change_model_from_email_change_app.py │ │ │ ├── 0013_employee_usos_id.py │ │ │ ├── 0024_auto_20201029_1347.py │ │ │ ├── 0023_auto_20200326_1613.py │ │ │ ├── 0014_student_usos_id.py │ │ │ ├── 0018_auto_20191209_1255.py │ │ │ ├── 0004_auto_20171019_1034.py │ │ │ ├── 0012_auto_20190206_2301.py │ │ │ ├── 0011_create_external_contractors_group.py │ │ │ ├── 0012_auto_20180804_2031.py │ │ │ ├── 0020_auto_20200202_1753.py │ │ │ ├── 0021_auto_20200219_2305.py │ │ │ ├── 0003_auto_20170601_1122.py │ │ │ ├── 0007_consents.py │ │ │ ├── 0009_auto_20180420_2334.py │ │ │ └── 0017_auto_20191020_1023.py │ │ ├── __init__.py │ │ ├── assets │ │ │ ├── components │ │ │ │ └── event-bus.js │ │ │ ├── consent-dialog.ts │ │ │ └── user-filter-list-index.js │ │ ├── context_processors.py │ │ ├── forms.py │ │ ├── signals.py │ │ ├── templates │ │ │ └── users │ │ │ │ └── form.html │ │ ├── urls.py │ │ └── apps.py │ └── notifications │ │ ├── tests │ │ ├── __init__.py │ │ └── test_serialization.py │ │ ├── migrations │ │ ├── __init__.py │ │ ├── 0008_notificationpreferencesteacher_thesis_has_been_accepted.py │ │ ├── 0006_notificationpreferencesteacher_thesis_voting_has_been_activated.py │ │ ├── 0002_auto_20180525_0559.py │ │ ├── 0009_auto_20240705_1517.py │ │ └── 0005_auto_20190929_1436.py │ │ ├── __init__.py │ │ ├── exceptions.py │ │ ├── apps.py │ │ ├── assets │ │ └── notifications-widget.js │ │ ├── templates │ │ └── notifications │ │ │ └── email_base.html │ │ ├── datatypes.py │ │ ├── forms.py │ │ ├── urls.py │ │ ├── utils.py │ │ └── custom_signals.py ├── scripts │ ├── __init__.py │ ├── mailscript.sh │ ├── timebonus.py │ ├── reassign_turns.py │ ├── random_enrollment.py │ ├── single_opening.py │ ├── enroll_tutoring_isim.py │ ├── ectsimport_new.py │ ├── copy_reservations.py │ └── clear_queues.py ├── zapisy │ ├── __init__.py │ ├── middleware │ │ └── __init__.py │ └── wsgi.py ├── mailer │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── retry_deferred.py │ │ │ └── send_mail.py │ ├── migrations │ │ └── __init__.py │ ├── admin.py │ └── utils.py ├── .yarnrc.yml ├── .prettierignore ├── locale │ └── en │ │ └── LC_MESSAGES │ │ └── django.mo ├── requirements.development.txt ├── requirements.production.txt ├── templates │ ├── grade │ │ └── _breadcrumbs.html │ ├── 405.html │ ├── 404.html │ ├── 403.html │ ├── 500.html │ ├── endless │ │ └── show_pages.html │ ├── maintenance_off.html │ └── semester_dropdown.html ├── requirements.test.txt ├── tsconfig.json ├── manage.py └── requirements.common.txt ├── infra ├── playbooks │ ├── ssl │ │ └── .gitkeep │ ├── templates │ │ ├── newrelic-infra.yml.j2 │ │ ├── ssl-signed.conf.j2 │ │ ├── backup.timer │ │ ├── django_cleanup.timer │ │ ├── newrelic-server-logs.yml.j2 │ │ ├── backup.service.j2 │ │ ├── django_cleanup.service.j2 │ │ ├── main.cf.j2 │ │ ├── rqworker-default.service.j2 │ │ ├── rqworker-notifications.service.j2 │ │ ├── nginx.service │ │ ├── gunicorn.service.j2 │ │ └── ssl-params.conf │ ├── dev │ │ ├── yarn.service │ │ ├── rqworker1.service │ │ ├── yarn_dev_watch.service │ │ ├── runserver.service │ │ └── rqworker2.service │ ├── update_ssl.yml │ └── newrelic.yml ├── hosts │ ├── production │ ├── staging │ ├── example │ └── Vagrantfile └── db_backups │ ├── anonymize.sql │ └── slack_notifications.py ├── .flake8 ├── .gitignore └── env └── .env_ci /zapisy/apps/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /infra/playbooks/ssl/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/news/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/scripts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/zapisy/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/api/rest/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/effects/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/grade/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/api/rest/v1/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/news/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedulersync/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/statistics/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/theses/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/users/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/mailer/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/mailer/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/zapisy/middleware/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/api/rest/v1/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/common/assets/tsfix/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/common/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/effects/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/news/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/desiderata/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/preferences/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedulersync/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/theses/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedulersync/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedulersync/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/mailer/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/grade/ticket_create/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/desiderata/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/preferences/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/timetable/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/grade/ticket_create/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/apps/schedulersync/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/.yarnrc.yml: -------------------------------------------------------------------------------- 1 | yarnPath: ".yarn/releases/yarn-berry.js" 2 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /zapisy/.prettierignore: -------------------------------------------------------------------------------- 1 | templates/ 2 | compiled_assets/ 3 | .yarn/* 4 | .pnp* 5 | -------------------------------------------------------------------------------- /zapisy/apps/theses/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.theses.apps.ThesesConfig' 2 | -------------------------------------------------------------------------------- /zapisy/apps/users/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.users.apps.UsersConfig' 2 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = "apps.grade.poll.apps.PollAppConfig" 2 | -------------------------------------------------------------------------------- /zapisy/apps/common/assets/main/types.ts: -------------------------------------------------------------------------------- 1 | export type UnconstrainedFunction = (...args: any[]) => any; 2 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.notifications.apps.NotificationsConfig' 2 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.enrollment.records.apps.RecordsAppConfig' 2 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/assets/bokeh.js: -------------------------------------------------------------------------------- 1 | import * as Bokeh from "@bokeh/bokehjs"; 2 | 3 | window.Bokeh = Bokeh; 4 | -------------------------------------------------------------------------------- /zapisy/apps/grade/ticket_create/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = "apps.grade.ticket_create.apps.TicketsAppConfig" 2 | -------------------------------------------------------------------------------- /zapisy/apps/users/assets/components/event-bus.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | export const EventBus = new Vue(); 3 | -------------------------------------------------------------------------------- /zapisy/apps/api/rest/v1/api_wrapper/sz_api/__init__.py: -------------------------------------------------------------------------------- 1 | from .sz_api import ZapisyApi 2 | 3 | __all__ = ['ZapisyApi'] 4 | -------------------------------------------------------------------------------- /zapisy/locale/en/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iiuni/projektzapisy/HEAD/zapisy/locale/en/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /zapisy/requirements.development.txt: -------------------------------------------------------------------------------- 1 | -r requirements.common.txt 2 | -r requirements.test.txt 3 | 4 | django-debug-toolbar==1.11.1 5 | -------------------------------------------------------------------------------- /zapisy/requirements.production.txt: -------------------------------------------------------------------------------- 1 | -r requirements.common.txt 2 | 3 | psycopg2==2.9.10 4 | slackclient==2.9.4 5 | dropbox==10.10.0 -------------------------------------------------------------------------------- /zapisy/apps/effects/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class EffectsConfig(AppConfig): 5 | name = 'effects' 6 | -------------------------------------------------------------------------------- /infra/playbooks/templates/newrelic-infra.yml.j2: -------------------------------------------------------------------------------- 1 | license_key: {{ newrelic_license_key }} 2 | display_name: System Zapisów ({{ deploy_env }}) 3 | -------------------------------------------------------------------------------- /zapisy/apps/common/static/common/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iiuni/projektzapisy/HEAD/zapisy/apps/common/static/common/images/favicon.ico -------------------------------------------------------------------------------- /zapisy/apps/schedule/static/schedule/feed-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iiuni/projektzapisy/HEAD/zapisy/apps/schedule/static/schedule/feed-icon.png -------------------------------------------------------------------------------- /zapisy/apps/schedule/assets/fullcalendar.scss: -------------------------------------------------------------------------------- 1 | #calendar { 2 | th { 3 | font-weight: normal; 4 | } 5 | a { 6 | color: var(--bs-dark); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .single_vote import SingleVote 2 | from .system_state import SystemState 3 | 4 | __all__ = ['SystemState', 'SingleVote'] 5 | -------------------------------------------------------------------------------- /infra/playbooks/templates/ssl-signed.conf.j2: -------------------------------------------------------------------------------- 1 | ssl_certificate /etc/ssl/certs/{{ deploy_server_name }}.crt; 2 | ssl_certificate_key /etc/ssl/private/{{ deploy_server_name }}.key; -------------------------------------------------------------------------------- /zapisy/apps/grade/ticket_create/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .rsa_keys import RSAKeys 2 | from .student_graded import StudentGraded 3 | 4 | __all__ = ['RSAKeys', 'StudentGraded'] 5 | -------------------------------------------------------------------------------- /zapisy/scripts/mailscript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source /home/zapisy/deploy/current/venv/bin/activate 3 | cd /home/zapisy/deploy/current/zapisy 4 | python manage.py send_mail 5 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/exceptions.py: -------------------------------------------------------------------------------- 1 | class DescriptionArgumentMissingException(Exception): 2 | pass 3 | 4 | 5 | class TitleArgumentMissingException(Exception): 6 | pass 7 | -------------------------------------------------------------------------------- /zapisy/apps/offer/preferences/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.main, name='preferences-main') 7 | ] 8 | -------------------------------------------------------------------------------- /zapisy/templates/grade/_breadcrumbs.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /zapisy/apps/grade/ticket_create/assets/katex.css: -------------------------------------------------------------------------------- 1 | /* @import "katex"; */ 2 | 3 | .katex { 4 | font-size: 1.35em !important; 5 | } 6 | p .katex { 7 | font-size: 1.2em !important; 8 | } 9 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/admin.py: -------------------------------------------------------------------------------- 1 | """Django admin panel for vote.""" 2 | from django.contrib import admin 3 | 4 | from .models import SystemState 5 | 6 | admin.site.register(SystemState) 7 | -------------------------------------------------------------------------------- /infra/playbooks/templates/backup.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Make a database backup 3 | 4 | [Timer] 5 | OnCalendar=*-*-* 01:00:00 6 | Persistent=true 7 | 8 | [Install] 9 | WantedBy=timers.target -------------------------------------------------------------------------------- /zapisy/apps/schedule/assets/report.css: -------------------------------------------------------------------------------- 1 | select[name="rooms"] { 2 | height: 10rem !important; 3 | } 4 | 5 | select[name="rooms"] option:hover { 6 | background-color: var(--bs-light); 7 | } 8 | -------------------------------------------------------------------------------- /zapisy/apps/offer/desiderata/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('change/', views.change_desiderata, name='change_desiderata'), 7 | ] 8 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/templates/schedule/emails/new_exam.txt: -------------------------------------------------------------------------------- 1 | W systemie zostały zarezerwowane sale dla przedmiotu {{ event.course }}. 2 | 3 | Link: https://zapisy.ii.uni.wroc.pl{% url 'events:session' %} 4 | -------------------------------------------------------------------------------- /zapisy/apps/common/static/common/images/data-consent/ecommerce-user-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iiuni/projektzapisy/HEAD/zapisy/apps/common/static/common/images/data-consent/ecommerce-user-profile.png -------------------------------------------------------------------------------- /zapisy/zapisy/wsgi.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.core.wsgi import get_wsgi_application 4 | 5 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "zapisy.settings") 6 | 7 | application = get_wsgi_application() 8 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .course_instance import CourseInstance 2 | from .group import Group 3 | from .semester import Semester 4 | 5 | __all__ = ['CourseInstance', 'Group', 'Semester'] 6 | -------------------------------------------------------------------------------- /zapisy/apps/common/redis.py: -------------------------------------------------------------------------------- 1 | import redis 2 | 3 | 4 | def flush_by_pattern(redis_client: redis.Redis, key_pattern: str) -> None: 5 | for key in redis_client.scan_iter(key_pattern): 6 | redis_client.delete(key) 7 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/templates/poll/_user_is_authenticated.html: -------------------------------------------------------------------------------- 1 |
2 | Uwaga! Aby ankieta pozostała anonimowa należy się wylogować. 3 |
-------------------------------------------------------------------------------- /zapisy/apps/schedule/templates/schedule/emails/new_exam.html: -------------------------------------------------------------------------------- 1 | W systemie zostały zarezerwowane sale dla przedmiotu {{ event.course }}. 2 | 3 | Link -------------------------------------------------------------------------------- /infra/playbooks/templates/django_cleanup.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Clear old django database sessions 3 | 4 | [Timer] 5 | OnCalendar=Wed *-*-* 05:00:00 6 | Persistent=true 7 | 8 | [Install] 9 | WantedBy=timers.target 10 | -------------------------------------------------------------------------------- /zapisy/apps/offer/desiderata/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from apps.offer.desiderata.models import Desiderata, DesiderataOther 4 | 5 | admin.site.register(Desiderata) 6 | admin.site.register(DesiderataOther) 7 | -------------------------------------------------------------------------------- /zapisy/apps/statistics/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('students/', views.students, name='students'), 7 | path('groups/', views.groups, name='groups'), 8 | ] 9 | -------------------------------------------------------------------------------- /zapisy/apps/news/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.all_news, name='news-all'), 7 | path('', views.all_news_focus_one, name='news-one'), 8 | ] 9 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class NotificationsConfig(AppConfig): 5 | name = "apps.notifications" 6 | 7 | def ready(self): 8 | import apps.notifications.signals # noqa 9 | -------------------------------------------------------------------------------- /zapisy/apps/theses/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ThesesConfig(AppConfig): 5 | name = 'apps.theses' 6 | verbose_name = 'Theses' 7 | 8 | def ready(self): 9 | from . import signals # noqa 10 | -------------------------------------------------------------------------------- /zapisy/apps/effects/templates/effects.html: -------------------------------------------------------------------------------- 1 | 2 | Zrealizowane grupy efektów kształcenia 3 | 4 | {% for effect in effects %} 5 | {{ effect }} 6 | {% endfor %} 7 | 8 | 9 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PollAppConfig(AppConfig): 5 | name = "apps.grade.poll" 6 | verbose_name = "Ocena zajęć" 7 | 8 | def ready(self): 9 | import apps.grade.poll.signals # noqa 10 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/models/__init__.py: -------------------------------------------------------------------------------- 1 | from apps.enrollment.records.models.opening_times import GroupOpeningTimes, T0Times 2 | from apps.enrollment.records.models.records import Record, RecordStatus 3 | 4 | __all__ = ['Record', 'RecordStatus', 'T0Times', 'GroupOpeningTimes'] 5 | -------------------------------------------------------------------------------- /infra/playbooks/dev/yarn.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Yarn install service 3 | 4 | [Service] 5 | User=vagrant 6 | Group=vagrant 7 | TimeoutStartSec=900 8 | WorkingDirectory=/vagrant/zapisy/ 9 | ExecStart=/usr/bin/yarnpkg 10 | Type=oneshot 11 | 12 | [Install] 13 | WantedBy=vagrant.mount -------------------------------------------------------------------------------- /zapisy/apps/grade/ticket_create/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class TicketsAppConfig(AppConfig): 5 | name = "apps.grade.ticket_create" 6 | verbose_name = "Tickets" 7 | 8 | def ready(self): 9 | import apps.grade.ticket_create.signals # noqa 10 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/templates/proposal/edit-proposal.html: -------------------------------------------------------------------------------- 1 | {% extends 'proposal/my-proposals.html' %} 2 | 3 | {% load crispy_forms_tags %} 4 | 5 | {% block all-content %} 6 | 7 |

Edycja propozycji przedmiotu

8 | {% crispy form %} 9 | 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /zapisy/apps/common/assets/tsfix/README.txt: -------------------------------------------------------------------------------- 1 | We need this file so we can specify it as the (only) input in tsconfig.json. 2 | Otherwise tsc will attempt to compile _ALL_ .ts files in the directory, 3 | regardless of whether they're specified as an entry (or imported by something that is) 4 | in webpack config. -------------------------------------------------------------------------------- /zapisy/apps/notifications/assets/notifications-widget.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Widget from "./components/Widget.vue"; 3 | 4 | new Vue({ 5 | el: "#notificationswidget", 6 | components: { 7 | Widget, 8 | }, 9 | render: function (h) { 10 | return h(Widget); 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /zapisy/requirements.test.txt: -------------------------------------------------------------------------------- 1 | -r requirements.common.txt 2 | 3 | # Reduce testing time by using binary psycopg2 package. 4 | psycopg2-binary==2.9.10 5 | 6 | factory_boy==2.12.0 7 | parameterized==0.8.1 8 | freezegun==0.3.15 9 | tblib 10 | 11 | flake8 12 | flake8-docstrings 13 | flake8-import-order 14 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/templates/notifications/email_base.html: -------------------------------------------------------------------------------- 1 | {{ greeting }}, 2 | 3 | {{ content|safe }} 4 | 5 | Zespół zapisy.ii.uni.wroc.pl 6 | 7 | --- 8 | Jeśli nie chcesz otrzymywać więcej powiadomień tego typu, zaktualizuj opcje 9 | w swoim profilu (https://zapisy.ii.uni.wroc.pl{% url 'my-profile' %}). 10 | -------------------------------------------------------------------------------- /infra/playbooks/dev/rqworker1.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Start rqworker default 3 | 4 | [Service] 5 | User=vagrant 6 | Group=vagrant 7 | WorkingDirectory=/vagrant/zapisy/ 8 | ExecStart=/home/vagrant/env3/bin/python3 /vagrant/zapisy/manage.py rqworker default 9 | Type=simple 10 | 11 | [Install] 12 | WantedBy=vagrant.mount -------------------------------------------------------------------------------- /infra/playbooks/dev/yarn_dev_watch.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Yarn dev:watch service 3 | After=yarn.service 4 | 5 | [Service] 6 | User=vagrant 7 | Group=vagrant 8 | TimeoutStartSec=900 9 | WorkingDirectory=/vagrant/zapisy/ 10 | ExecStart=/usr/bin/yarnpkg dev:watch 11 | Type=simple 12 | 13 | [Install] 14 | WantedBy=vagrant.mount -------------------------------------------------------------------------------- /zapisy/apps/grade/ticket_create/assets/ticketsgenerate_main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import TicketsGenerator from "./TicketsGenerator.vue"; 3 | 4 | Vue.config.productionTip = false; 5 | 6 | window.onload = function () { 7 | new Vue({ 8 | el: "#app", 9 | render: (h) => h(TicketsGenerator), 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /infra/hosts/production: -------------------------------------------------------------------------------- 1 | [deploy] 2 | server1 3 | 4 | [vault] 5 | server1 6 | 7 | [deploy:vars] 8 | ansible_host=zapisy.ii.uni.wroc.pl 9 | ansible_port=22 10 | ansible_user= 11 | ansible_ssh_private_key_file= 12 | deploy_env=production 13 | deploy_user=zapisy 14 | deploy_version=master 15 | deploy_server_name=zapisy.ii.uni.wroc.pl 16 | -------------------------------------------------------------------------------- /infra/playbooks/templates/newrelic-server-logs.yml.j2: -------------------------------------------------------------------------------- 1 | logs: 2 | - name: django-zapisy-logs 3 | file: /home/{{ deploy_user }}/deploy/current/zapisy/logs/djangoproject.log 4 | 5 | - name: nginx-logs 6 | systemd: nginx 7 | attributes: 8 | logtype: nginx 9 | 10 | - name: backup-logs 11 | systemd: backup 12 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/assets/reservation-widget.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import ClassroomPicker from "./components/ClassroomPicker.vue"; 3 | 4 | new Vue({ 5 | el: "#reservation-widget", 6 | components: { 7 | ClassroomPicker, 8 | }, 9 | render: function (h) { 10 | return h(ClassroomPicker); 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /infra/playbooks/dev/runserver.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Start server 3 | 4 | [Service] 5 | Restart=always 6 | User=vagrant 7 | Group=vagrant 8 | WorkingDirectory=/vagrant/zapisy/ 9 | ExecStart=/home/vagrant/env3/bin/python3 /vagrant/zapisy/manage.py runserver 0.0.0.0:8000 10 | Type=simple 11 | 12 | [Install] 13 | WantedBy=vagrant.mount -------------------------------------------------------------------------------- /zapisy/apps/common/templates/widgets/markdown-editor.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/utils.py: -------------------------------------------------------------------------------- 1 | def mailto(author, students, bcc=False): 2 | """Helper method to create mailto links.""" 3 | result = author.email 4 | 5 | if students: 6 | return result + ('?bcc=' if bcc else ',') + \ 7 | ','.join([student.user.email.replace('+', '%2B') for student in students]) 8 | return result 9 | -------------------------------------------------------------------------------- /zapisy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "experimentalDecorators": true, 5 | "target": "ESNext", 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "noUnusedLocals": true, 9 | "baseUrl": ".", 10 | "paths": { 11 | "@/*": ["apps/*"] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /infra/hosts/staging: -------------------------------------------------------------------------------- 1 | [deploy] 2 | server1 3 | 4 | [vault] 5 | server1 6 | 7 | [deploy:vars] 8 | ansible_host=zapisy-staging.ii.uni.wroc.pl 9 | ansible_port=22 10 | ansible_user= 11 | ansible_ssh_private_key_file= 12 | deploy_env=staging 13 | deploy_user=zapisy 14 | deploy_version=master-dev 15 | deploy_server_name=zapisy-staging.ii.uni.wroc.pl 16 | -------------------------------------------------------------------------------- /infra/playbooks/dev/rqworker2.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Start rqworker dispatch-notifications 3 | 4 | [Service] 5 | User=vagrant 6 | Group=vagrant 7 | WorkingDirectory=/vagrant/zapisy/ 8 | ExecStart=/home/vagrant/env3/bin/python3 /vagrant/zapisy/manage.py rqworker dispatch-notifications 9 | Type=simple 10 | 11 | [Install] 12 | WantedBy=vagrant.mount -------------------------------------------------------------------------------- /zapisy/apps/grade/ticket_create/assets/katex.ts: -------------------------------------------------------------------------------- 1 | import "katex/dist/katex.css"; 2 | import "./katex.css"; 3 | 4 | import renderMathInElement from "katex/dist/contrib/auto-render"; 5 | 6 | document.addEventListener("DOMContentLoaded", () => { 7 | const element = document.getElementById("od-vote-main-rules")!; 8 | renderMathInElement(element); 9 | }); 10 | -------------------------------------------------------------------------------- /zapisy/apps/theses/assets/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | 4 | import theses from "./theses"; 5 | import filters from "./filters"; 6 | import sorting from "./sorting"; 7 | 8 | Vue.use(Vuex); 9 | 10 | export default new Vuex.Store({ 11 | modules: { 12 | theses, 13 | filters, 14 | sorting, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/templates/proposal/fields/custom_checkbox.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 |
4 |
5 | {% crispy_field field 'class' 'custom-control-input' %} 6 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /zapisy/apps/statistics/assets/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | 4 | import courses from "./courses"; 5 | import filters from "./filters"; 6 | import sorting from "./sorting"; 7 | 8 | Vue.use(Vuex); 9 | 10 | export default new Vuex.Store({ 11 | modules: { 12 | courses, 13 | filters, 14 | sorting, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /infra/hosts/example: -------------------------------------------------------------------------------- 1 | [deploy] 2 | server1 3 | 4 | [deploy:vars] 5 | ansible_host=192.168.33.10 6 | ansible_port=22 7 | ansible_user=vagrant 8 | ansible_ssh_private_key_file=hosts/.vagrant/machines/default/virtualbox/private_key 9 | deploy_env=test 10 | deploy_user=zapisy 11 | deploy_version=master-dev 12 | deploy_server_name=192.168.33.10 13 | deploy_hostname=ubuntu-local 14 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/tests/factories.py: -------------------------------------------------------------------------------- 1 | from apps.enrollment.courses.tests.factories import CourseInformationFactory 2 | from apps.offer.proposal.models import Proposal 3 | 4 | __all__ = ['ProposalFactory', ] 5 | 6 | 7 | class ProposalFactory(CourseInformationFactory): 8 | """Creates a new Proposal instance.""" 9 | class Meta: 10 | model = Proposal 11 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0015_merge_20190321_1311.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.8 on 2019-03-21 13:11 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0014_student_usos_id'), 10 | ('users', '0012_auto_20190206_2301'), 11 | ] 12 | 13 | operations = [ 14 | ] 15 | -------------------------------------------------------------------------------- /infra/playbooks/templates/backup.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Make a database backup 3 | 4 | [Service] 5 | User={{ deploy_user }} 6 | Group={{ deploy_user }} 7 | WorkingDirectory=/home/{{ deploy_user }}/deploy/current/zapisy/ 8 | ExecStart=/home/{{ deploy_user }}/deploy/current/venv/bin/python3 /home/{{ deploy_user }}/deploy/current/infra/db_backups/backup.py 9 | Type=oneshot 10 | -------------------------------------------------------------------------------- /zapisy/apps/grade/ticket_create/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('get-poll-data', views.get_poll_data, name='grade-ticket-get-poll-data'), 7 | path('sign-tickets', views.sign_tickets, name='grade-ticket-sign-tickets'), 8 | path('tickets-generate', views.tickets_generate, name='grade-ticket-tickets-generate') 9 | ] 10 | -------------------------------------------------------------------------------- /zapisy/apps/users/assets/consent-dialog.ts: -------------------------------------------------------------------------------- 1 | // Displays the data consent modal right when the page loads. 2 | import * as bootstrap from "bootstrap"; 3 | 4 | window.addEventListener("load", () => { 5 | const modal = document.getElementById("consentDialog"); 6 | 7 | if (modal) { 8 | const bootstrapModal = new bootstrap.Modal(modal, {}); 9 | bootstrapModal.show(); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0016_merge_20190313_1524.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.8 on 2019-03-13 15:24 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0015_auto_20190312_1330'), 10 | ('courses', '0015_remove_course_teachers'), 11 | ] 12 | 13 | operations = [ 14 | ] 15 | -------------------------------------------------------------------------------- /infra/playbooks/templates/django_cleanup.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Clear old django database sessions 3 | 4 | [Service] 5 | User={{ deploy_user }} 6 | Group={{ deploy_user }} 7 | WorkingDirectory=/home/{{ deploy_user }}/deploy/current/zapisy/ 8 | ExecStart=/home/{{ deploy_user }}/deploy/current/venv/bin/python3 /home/{{ deploy_user }}/deploy/current/zapisy/manage.py clearsessions 9 | Type=oneshot 10 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/urls.py: -------------------------------------------------------------------------------- 1 | """Enrollment actions.""" 2 | 3 | from django.urls import path 4 | 5 | from apps.enrollment.records import views 6 | 7 | urlpatterns = [ 8 | path('enqueue/', views.enqueue, name='records-enqueue'), 9 | path('dequeue/', views.dequeue, name='records-dequeue'), 10 | path('queue-set-priority/', views.queue_set_priority, name='records-set-priority'), 11 | ] 12 | -------------------------------------------------------------------------------- /zapisy/apps/offer/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import include, path 2 | 3 | urlpatterns = [ 4 | path('assignments/', include('apps.offer.assignments.urls')), 5 | path('desiderata/', include('apps.offer.desiderata.urls')), 6 | path('preferences/', include('apps.offer.preferences.urls')), 7 | path('vote/', include('apps.offer.vote.urls')), 8 | path('', include('apps.offer.proposal.urls')), 9 | ] 10 | -------------------------------------------------------------------------------- /zapisy/templates/405.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block main-subtitle %}Błąd 405{% endblock %} 4 | 5 | {% block all-content %} 6 |

Błąd 405

7 |

Nieprawidłowe zapytanie. Prosimy, 8 | zgłoś ten błąd na trackerze Zapisów. 9 |

10 |

Powrót do strony głównej.

11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/templates/forms/custom_visible_checkbox.html: -------------------------------------------------------------------------------- 1 | {% load crispy_forms_field %} 2 | 3 |
4 |
5 | {% crispy_field field 'class' 'custom-control-input' %} 6 | 7 | {{ field.help_text }} 8 |
9 |
-------------------------------------------------------------------------------- /zapisy/apps/users/context_processors.py: -------------------------------------------------------------------------------- 1 | from apps.users.models import is_employee, is_external_contractor, is_student 2 | 3 | 4 | def roles(request): 5 | """Merge user's group membership info into template context.""" 6 | return { 7 | 'is_employee': is_employee(request.user), 8 | 'is_external_contractor': is_external_contractor(request.user), 9 | 'is_student': is_student(request.user), 10 | } 11 | -------------------------------------------------------------------------------- /zapisy/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block main-subtitle %}Błąd 404{% endblock %} 4 | 5 | {% block all-content %} 6 |

Błąd 404

7 |

Nie znaleziono strony. Jeżeli znalazłeś martwy link, 8 | zgłoś ten fakt na trackerze Zapisów. 9 |

10 |

Powrót do strony głównej.

11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /infra/playbooks/templates/main.cf.j2: -------------------------------------------------------------------------------- 1 | myhostname = {{ deploy_hostname | default(deploy_server_name) }} 2 | 3 | biff = no 4 | compatibility_level = 2 5 | 6 | smtp_tls_security_level = encrypt 7 | smtp_tls_CApath = /etc/ssl/certs 8 | 9 | # We are using GSuite SMTP Relay to make our emails more credible. 10 | # https://support.google.com/a/answer/2956491 11 | relayhost = [smtp-relay.gmail.com]:587 12 | 13 | inet_interfaces = loopback-only 14 | -------------------------------------------------------------------------------- /infra/playbooks/templates/rqworker-default.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Start rqworker default 3 | [Service] 4 | User={{ deploy_user }} 5 | Group={{ deploy_user }} 6 | WorkingDirectory=/home/{{ deploy_user }}/deploy/current/zapisy/ 7 | ExecStart=/home/{{ deploy_user }}/deploy/current/venv/bin/python3 /home/{{ deploy_user }}/deploy/current/zapisy/manage.py rqworker default 8 | Type=simple 9 | 10 | [Install] 11 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.vote_main, name='vote-main'), 7 | path('view/', views.my_vote, name='my-vote-view'), 8 | path('vote/', views.vote, name='vote'), 9 | path('summary', views.vote_summary, name='vote-summary'), 10 | path('summary//', views.proposal_vote_summary, name='proposal-vote-summary') 11 | ] 12 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/apps.py: -------------------------------------------------------------------------------- 1 | """Django app config for enrollment.records. 2 | 3 | (See https://docs.djangoproject.com/en/2.0/ref/applications/). 4 | """ 5 | 6 | from django.apps import AppConfig 7 | 8 | 9 | class RecordsAppConfig(AppConfig): 10 | name = 'apps.enrollment.records' 11 | 12 | def ready(self): 13 | import apps.enrollment.records.signals # noqa 14 | import apps.enrollment.records.tasks # noqa 15 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/signals.py: -------------------------------------------------------------------------------- 1 | """This module will define signals triggering asynchronous actions. 2 | 3 | Every user of the `enrollment.records` app can send this signal instead of 4 | running the job directly. This will be useful for testing, where we can patch 5 | the signal receiver. 6 | """ 7 | from django.dispatch import Signal 8 | 9 | # Signal senders must provide a `group_id` argument. 10 | GROUP_CHANGE_SIGNAL = Signal() 11 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/templates/schedule/reports/base.html: -------------------------------------------------------------------------------- 1 | {% extends 'schedule/base.html' %} 2 | 3 | {% block events_report %} class="active"{% endblock %} 4 | 5 | {% block bread %} 6 | 7 | 8 | 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /zapisy/templates/403.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block main-subtitle %}Błąd 403{% endblock %} 4 | 5 | {% block all-content %} 6 |

Błąd 403, brak dostępu

7 |

Niestety, nie masz dostępu. Jeśli uważasz, że powinieneś mieć dostęp, 8 | zgłoś ten fakt administratorowi Systemu Zapisów. 9 |

10 |

Powrót do strony głównej.

11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/migrations/0005_remove_singlevote_entity.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-05-07 13:28 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('vote', '0004_data_migration'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='singlevote', 15 | name='entity', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /zapisy/apps/common/days_of_week.py: -------------------------------------------------------------------------------- 1 | MONDAY = '1' 2 | TUESDAY = '2' 3 | WEDNESDAY = '3' 4 | THURSDAY = '4' 5 | FRIDAY = '5' 6 | SATURDAY = '6' 7 | SUNDAY = '7' 8 | 9 | DAYS_OF_WEEK = [(MONDAY, 'poniedziałek'), 10 | (TUESDAY, 'wtorek'), 11 | (WEDNESDAY, 'środa'), 12 | (THURSDAY, 'czwartek'), 13 | (FRIDAY, 'piątek'), 14 | (SATURDAY, 'sobota'), 15 | (SUNDAY, 'niedziela')] 16 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0015_remove_course_teachers.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.8 on 2019-03-07 17:01 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0014_auto_20190212_1313'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='course', 15 | name='teachers', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0029_remove_term_classroom.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.14 on 2019-11-04 13:46 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0028_guaranteedspots'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='term', 15 | name='classroom', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0016_remove_program_type_of_points.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-05-17 16:37 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0015_merge_20190321_1311'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='program', 15 | name='type_of_points', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /infra/playbooks/templates/rqworker-notifications.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Start rqworker dispatch-notifications 3 | [Service] 4 | User={{ deploy_user }} 5 | Group={{ deploy_user }} 6 | WorkingDirectory=/home/{{ deploy_user }}/deploy/current/zapisy/ 7 | ExecStart=/home/{{ deploy_user }}/deploy/current/venv/bin/python3 /home/{{ deploy_user }}/deploy/current/zapisy/manage.py rqworker dispatch-notifications 8 | Type=simple 9 | 10 | [Install] 11 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /zapisy/apps/notifications/datatypes.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Dict 3 | 4 | 5 | class Notification: 6 | 7 | def __init__(self, id: str, issued_on: datetime, description_id: str, 8 | description_args: Dict, target: str = "#"): 9 | self.id = id 10 | self.issued_on = issued_on 11 | self.description_id = description_id 12 | self.description_args = description_args 13 | self.target = target 14 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/forms.py: -------------------------------------------------------------------------------- 1 | from django.forms import ModelForm 2 | 3 | from apps.notifications.models import NotificationPreferencesStudent, NotificationPreferencesTeacher 4 | 5 | 6 | class PreferencesFormStudent(ModelForm): 7 | class Meta: 8 | model = NotificationPreferencesStudent 9 | exclude = ['user'] 10 | 11 | 12 | class PreferencesFormTeacher(ModelForm): 13 | class Meta: 14 | model = NotificationPreferencesTeacher 15 | exclude = ['user'] 16 | -------------------------------------------------------------------------------- /zapisy/apps/common/assets/main/expose_libs.ts: -------------------------------------------------------------------------------- 1 | import $ from "jquery"; 2 | import Popper from "popper.js"; 3 | import * as bootstrap from "bootstrap"; 4 | 5 | (window as any).$ = $; 6 | (window as any).jQuery = $; 7 | (window as any).Popper = Popper; 8 | 9 | window.addEventListener("load", () => { 10 | Array.from(document.querySelectorAll('[data-bs-toggle="popover"]')).map( 11 | function (popoverTriggerEl) { 12 | return new bootstrap.Popover(popoverTriggerEl); 13 | } 14 | ); 15 | }); 16 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/templates/schedule/history.html: -------------------------------------------------------------------------------- 1 | {% extends 'schedule/reservations.html' %} 2 | 3 | {% block bread %} 4 | 5 | 6 | 7 | {% endblock %} 8 | 9 | {% block schedule_manage %}{% endblock %} 10 | {% block schedule_history %} class="active"{% endblock %} 11 | -------------------------------------------------------------------------------- /zapisy/apps/users/assets/user-filter-list-index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | 3 | import UserFilter from "./components/UserFilter.vue"; 4 | import UserList from "./components/UserList.vue"; 5 | 6 | if (document.getElementById("user-filter") !== null) { 7 | new Vue({ 8 | el: "#user-filter", 9 | render: (h) => h(UserFilter), 10 | }); 11 | } 12 | if (document.getElementById("user-list") !== null) { 13 | new Vue({ 14 | el: "#user-list", 15 | render: (h) => h(UserList), 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/templates/courses/course_parts/course_info.html: -------------------------------------------------------------------------------- 1 | {% include "courses/course_parts/course_head.html" %} 2 |
3 | {% regroup groups|dictsort:"type" by type as groups_by_type %} 4 | 5 | {% for class_type, class_groups in groups_by_type %} 6 | {% include "courses/course_parts/groups_section.html" with class_type=class_type class_groups=class_groups waiting_students=waiting_students %} 7 | {% endfor %} 8 | 9 |
10 | {% include "courses/course_parts/course_consultations.html" %} 11 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0004_auto_20171019_1034.py: -------------------------------------------------------------------------------- 1 | from django.db import models, migrations 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('courses', '0003_auto_20170606_1842'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions( 12 | name='courseentity', 13 | options={'ordering': ['name_pl'], 'verbose_name': 'Podstawa przedmiotu', 'verbose_name_plural': 'Podstawy przedmiot\xf3w'}, 14 | ), 15 | ] 16 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/migrations/0004_auto_20170606_1842.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('poll', '0003_auto_20170601_1122'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name='multiplechoicequestionanswer', 13 | name='options', 14 | field=models.ManyToManyField(to='poll.Option', verbose_name=b'odpowiedzi', blank=True), 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/migrations/0007_proposal_semester.py: -------------------------------------------------------------------------------- 1 | import apps.offer.proposal.models 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('proposal', '0006_proposal_semester'), 9 | ('courses', '0018_remove_courseinformation_semester'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField( 14 | 'proposal', 15 | 'smtr', 16 | 'semester', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/migrations/0009_term_ignore_conflicts.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.15 on 2019-12-17 15:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('schedule', '0008_auto_20191119_0017'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='term', 15 | name='ignore_conflicts', 16 | field=models.BooleanField(default=False), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/templatetags/schedule_filters.py: -------------------------------------------------------------------------------- 1 | """Template Tags useful in schedule views.""" 2 | from django import template 3 | 4 | register = template.Library() 5 | 6 | 7 | @register.filter(is_safe=True) 8 | def number_to_weekday(value: int): 9 | """Translates a number into a weekday.""" 10 | types_dict = { 11 | 1: "pn", 12 | 2: "wt", 13 | 3: "śr", 14 | 4: "cz", 15 | 5: "pt", 16 | 6: "so", 17 | 7: "ni", 18 | } 19 | return types_dict.get(value, "") 20 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0008_auto_20180601_1918.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-06-01 19:18 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('users', '0007_consents'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameModel( 16 | old_name='Consents', 17 | new_name='PersonalDataConsent', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0005_auto_20171019_1044.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | import django.contrib.auth.models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('users', '0004_auto_20171019_1034'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterModelManagers( 13 | name='extendeduser', 14 | managers=[ 15 | ('objects', django.contrib.auth.models.UserManager()), 16 | ], 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0018_remove_courseinformation_semester.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-05-12 22:12 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0017_courseinformation'), 10 | ('proposal', '0006_proposal_semester') 11 | ] 12 | 13 | operations = [ 14 | migrations.RemoveField( 15 | model_name='courseinformation', 16 | name='semester', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/templatetags/vote_filters.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | from apps.offer.proposal.models import SemesterChoices 4 | 5 | register = template.Library() 6 | 7 | 8 | @register.filter 9 | def semester_order(proposals): 10 | """Arranges semesters in order in which they actually occur.""" 11 | ordering = { 12 | SemesterChoices.WINTER: 1, 13 | SemesterChoices.SUMMER: 2, 14 | SemesterChoices.UNASSIGNED: 3 15 | } 16 | return sorted(proposals, key=lambda p: ordering[p.semester]) 17 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/migrations/0003_auto_20170601_1122.py: -------------------------------------------------------------------------------- 1 | from django.db import models, migrations 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('schedule', '0002_auto_20170529_1617'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name='event', 13 | name='visible', 14 | field=models.BooleanField(default=False, verbose_name='Wydarzenie jest publiczne'), 15 | preserve_default=True, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/migrations/0003_auto_20170606_1842.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('proposal', '0002_auto_20170529_1617'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name='syllabus', 13 | name='learning_methods', 14 | field=models.ManyToManyField(to='proposal.LearningMethod', verbose_name='Metody kszta\u0142cenia', blank=True), 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0019_remove_email_change_model_from_email_change_app.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.13 on 2019-12-10 14:55 2 | 3 | from django.db import migrations 4 | 5 | DROP_EMAIL_CHANGE_REQUEST_TABLE = """\ 6 | DROP TABLE IF EXISTS email_change_emailchangerequest CASCADE; 7 | """ 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('users', '0018_auto_20191209_1255'), 14 | ] 15 | 16 | operations = [ 17 | migrations.RunSQL(DROP_EMAIL_CHANGE_REQUEST_TABLE) 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/mailer/management/commands/retry_deferred.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from django.core.management.base import BaseCommand 3 | from mailer.models import Message 4 | 5 | logger = logging.getLogger('mailer.retry_deferred') 6 | 7 | 8 | class Command(BaseCommand): 9 | help = 'Attempt to resend any deferred mail.' 10 | 11 | def handle(self, *args, **options): 12 | logger.info("-" * 72) 13 | count = Message.objects.retry_deferred() # @@@ new_priority not yet supported 14 | logger.info("%s message(s) retried" % count) 15 | -------------------------------------------------------------------------------- /zapisy/apps/offer/preferences/migrations/0004_remove_preference_hidden.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-05-25 22:59 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('preferences', '0003_auto_20180525_0559'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='preference', 17 | name='hidden', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/tests/factories.py: -------------------------------------------------------------------------------- 1 | import factory 2 | from factory.django import DjangoModelFactory 3 | 4 | from apps.enrollment.courses.tests.factories import GroupFactory 5 | from apps.users.tests.factories import StudentFactory 6 | 7 | from ..models.records import Record, RecordStatus 8 | 9 | 10 | class RecordFactory(DjangoModelFactory): 11 | class Meta: 12 | model = Record 13 | 14 | group = factory.SubFactory(GroupFactory) 15 | student = factory.SubFactory(StudentFactory) 16 | status = RecordStatus.ENROLLED 17 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0013_employee_usos_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.8 on 2019-03-06 22:46 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0012_auto_20180804_2031'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='employee', 15 | name='usos_id', 16 | field=models.PositiveIntegerField(blank=True, null=True, verbose_name='ID w USOSie'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0024_auto_20201029_1347.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.2 on 2020-10-29 13:47 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0023_auto_20200326_1613'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='personaldataconsent', 15 | name='granted', 16 | field=models.BooleanField(null=True, verbose_name='zgoda udzielona'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/migrations/0011_auto_20201106_2029.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2020-11-06 20:29 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('records', '0010_auto_20190822_1718'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='record', 15 | name='status', 16 | field=models.IntegerField(choices=[(0, 'Queued'), (1, 'Enrolled'), (2, 'Removed')]), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/grade/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import include, path 2 | 3 | from .poll import views as poll_views 4 | 5 | # to tree/list_view and description, pass by GET: 6 | # format=json - returns data as json 7 | # format=html - returns data rendered with template, not using the base template 8 | # by default, they return a fully rendered page 9 | 10 | urlpatterns = [ 11 | path("", poll_views.GradeDetails.as_view(), name="grade-main"), 12 | path("poll/", include("apps.grade.poll.urls")), 13 | path("ticket/", include("apps.grade.ticket_create.urls")), 14 | ] 15 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/migrations/0004_auto_20170606_1842.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('schedule', '0003_auto_20170601_1122'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterModelOptions( 12 | name='eventmoderationmessage', 13 | options={'ordering': ['created'], 'get_latest_by': 'created', 'verbose_name': 'wiadomo\u015b\u0107 wydarzenia', 'verbose_name_plural': 'wiadomo\u015bci wydarzenia'}, 14 | ), 15 | ] 16 | -------------------------------------------------------------------------------- /zapisy/apps/theses/migrations/0005_auto_20201106_2039.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2020-11-06 20:39 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('theses', '0004_migrate_votes_and_remarks'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='thesis', 15 | name='id', 16 | field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0023_auto_20200326_1613.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.15 on 2020-03-26 16:13 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0022_auto_20200324_2042'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='student', 15 | name='status', 16 | ), 17 | migrations.RemoveField( 18 | model_name='student', 19 | name='t0', 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | # Urls: "get", "count", "delete" and "delete/all" 6 | # are in charge of manage notifications in Widget.vue 7 | 8 | app_name = "notifications" 9 | urlpatterns = [ 10 | path('get', views.get_notifications, name='get_notifications'), 11 | path('delete', views.delete_one, name='delete-one-notification'), 12 | path('delete/all', views.delete_all, name='delete-all-notifications'), 13 | path('preferences/save', views.preferences_save, name='preferences-save'), 14 | ] 15 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/assets/course-list-index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | 4 | import CourseList from "./components/CourseList.vue"; 5 | import CourseFilter from "./components/CourseFilter.vue"; 6 | import filters from "@/enrollment/timetable/assets/store/filters"; 7 | 8 | Vue.use(Vuex); 9 | 10 | const store = new Vuex.Store({ 11 | modules: { 12 | filters, 13 | }, 14 | }); 15 | 16 | new Vue({ el: "#course-filter", render: (h) => h(CourseFilter), store }); 17 | new Vue({ el: "#course-list", render: (h) => h(CourseList), store }); 18 | -------------------------------------------------------------------------------- /zapisy/apps/common/assets/main/sidebar-fold.js: -------------------------------------------------------------------------------- 1 | // Implements folding sidebar contents on small devices. 2 | 3 | function toggleSidebarFolded(event) { 4 | const sidebarInner = document.getElementById("sidebar-inner"); 5 | sidebarInner.classList.toggle("folded"); 6 | event.preventDefault(); 7 | } 8 | 9 | document.addEventListener("DOMContentLoaded", function () { 10 | const sidebarFoldButton = document.getElementById("fold-toggler"); 11 | if (!sidebarFoldButton) { 12 | return; 13 | } 14 | sidebarFoldButton.addEventListener("click", toggleSidebarFolded); 15 | }); 16 | -------------------------------------------------------------------------------- /zapisy/apps/offer/desiderata/migrations/0004_auto_20171216_2350.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.9.13 on 2017-12-16 23:50 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('desiderata', '0003_auto_20171112_2306'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='desiderataother', 16 | name='comment', 17 | field=models.TextField(default=b'', max_length=1000, verbose_name=b'uwagi'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /zapisy/apps/schedulersync/migrations/0002_auto_20180201_2236.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.10.8 on 2018-02-01 22:36 2 | 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('schedulersync', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='termsyncdata', 16 | name='scheduler_id', 17 | field=models.PositiveIntegerField(null=True, verbose_name='id grupy w schedulerze'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /zapisy/apps/schedulersync/migrations/0004_delete_redundant_tables.py: -------------------------------------------------------------------------------- 1 | # This migration is for removing redundant tables from database 2 | # It has nothing to do with schedulersync app and its models 3 | 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('schedulersync', '0003_coursemap_employeemap'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RunSQL('DROP TABLE IF EXISTS south_migrationhistory;'), 15 | migrations.RunSQL('DROP TABLE IF EXISTS auth_message;'), 16 | ] 17 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0014_student_usos_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.8 on 2019-03-08 21:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0013_employee_usos_id'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='student', 15 | name='usos_id', 16 | field=models.PositiveIntegerField(blank=True, null=True, unique=True, verbose_name='Kod studenta w systemie USOS'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/timetable/assets/components/PrototypeTimetable.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/timetable/assets/components/PrototypeDay.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0018_auto_20191209_1255.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.13 on 2019-12-09 12:55 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0017_auto_20191020_1023'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='employee', 15 | name='last_news_view', 16 | ), 17 | migrations.RemoveField( 18 | model_name='student', 19 | name='last_news_view', 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0026_courseinformation_discipline.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-08-03 20:42 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0025_type_obligatory'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='courseinformation', 15 | name='discipline', 16 | field=models.CharField(default='Informatyka', max_length=100, verbose_name='dyscyplina'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | max-doc-length = 99 4 | exclude = 5 | node_modules, 6 | *migrations, 7 | scripts, 8 | db_backups, 9 | # mailer app will be removed soon. 10 | mailer, 11 | docstring-convention = google 12 | import-order-style = pep8 13 | application-import-names = apps, zapisy 14 | extend-ignore = 15 | # Do not enforce docstrings in every function. 16 | D1, 17 | # It is perfectly valid to only describe the unobvious arguments. 18 | D417, 19 | # flake8-import-order treats relative imports differently from isort. 20 | I202, 21 | -------------------------------------------------------------------------------- /zapisy/apps/news/migrations/0003_markdown.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | from html2text import html2text as markdownify 3 | 4 | 5 | def migrate_news_to_markdown(apps, schema_editor): 6 | News = apps.get_model('news', 'News') 7 | for news in News.objects.all(): 8 | news.body = markdownify(news.body) 9 | news.save() 10 | 11 | 12 | class Migration(migrations.Migration): 13 | dependencies = [ 14 | ('news', '0002_auto_20180525_0559'), 15 | ] 16 | 17 | operations = [ 18 | migrations.operations.RunPython(migrate_news_to_markdown), 19 | ] 20 | -------------------------------------------------------------------------------- /zapisy/apps/theses/assets/theses-widget.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import ThesesList from "./components/ThesesList.vue"; 3 | import ThesisFilter from "./components/ThesisFilter.vue"; 4 | import store from "./store"; 5 | 6 | new Vue({ 7 | el: "#theses-filter", 8 | components: { 9 | ThesisFilter, 10 | }, 11 | render: function (h) { 12 | return h(ThesisFilter); 13 | }, 14 | store, 15 | }); 16 | 17 | new Vue({ 18 | el: "#theses-list", 19 | components: { 20 | ThesesList, 21 | }, 22 | render: function (h) { 23 | return h(ThesesList); 24 | }, 25 | store, 26 | }); 27 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0004_auto_20171019_1034.py: -------------------------------------------------------------------------------- 1 | from django.db import models, migrations 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('users', '0003_auto_20170601_1122'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name='openingtimesview', 13 | name='student', 14 | field=models.OneToOneField(related_name='opening_times', primary_key=True, serialize=False, to='users.Student', on_delete=models.CASCADE), 15 | preserve_default=True, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0031_auto_20200529_1126.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.6 on 2020-05-29 11:26 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0030_auto_20200420_1026'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='semester', 15 | name='desiderata_closing', 16 | ), 17 | migrations.RemoveField( 18 | model_name='semester', 19 | name='desiderata_opening', 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /zapisy/apps/api/rest/v1/api_wrapper/README.md: -------------------------------------------------------------------------------- 1 | # sz_api 2 | 3 | API wrapper for https://zapisy.ii.uni.wroc.pl/ 4 | 5 | ## Installation: 6 | 7 | python3 -m pip install --user git+ssh://git@github.com/iiuni/projektzapisy.git#egg=sz_api\&subdirectory=zapisy/apps/api/rest/v1/api_wrapper 8 | 9 | ## Example: 10 | 11 | ```python 12 | from sz_api import ZapisyApi 13 | api = ZapisyApi('Token valid_key') 14 | for semester in api.semesters(): 15 | print(semester.display_name) 16 | ``` 17 | 18 | ## Troubleshooting: 19 | 20 | before opening an issue check that: 21 | 22 | 1. token is a string beginning with "Token " 23 | -------------------------------------------------------------------------------- /zapisy/apps/offer/desiderata/assets/checkboxes-toggling.js: -------------------------------------------------------------------------------- 1 | import { checkboxes } from "checkboxes.js/dist/jquery.checkboxes-1.2.2.js"; 2 | 3 | $.checkboxes = checkboxes; 4 | 5 | // Enable range toggling. 6 | $("table").checkboxes("range", true, "tr :checkbox"); 7 | 8 | // Enable row toggling. 9 | $(".day-toggle").on("click", (e) => { 10 | e.preventDefault(); 11 | const row = $(e.target).closest("tr"); 12 | const checkboxes = row.find("[type='checkbox']").toArray(); 13 | const state = checkboxes.some((ch) => ch.checked); 14 | checkboxes.forEach((c) => { 15 | c.checked = !state; 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | infra/hosts/*.conf 3 | infra/playbooks/*.env 4 | infra/playbooks/*.7z 5 | infra/playbooks/ssl/*.crt 6 | infra/playbooks/ssl/*.key 7 | *.pyc 8 | *.swp 9 | .project 10 | .pydevproject 11 | .settings/ 12 | search_index/* 13 | *.tmp 14 | *.log 15 | *~ 16 | .idea/ 17 | .vagrant/ 18 | .DS_Store 19 | zapisy/logs/ 20 | personal/ 21 | .vscode/ 22 | node_modules 23 | zapisy/.yarn/* 24 | !zapisy/.yarn/releases 25 | !zapisy/.yarn/plugins 26 | !zapisy/.yarn/versions 27 | .pnp* 28 | zapisy/static/ 29 | compiled_assets/ 30 | zapisy/webpack_resources/webpack-stats.json 31 | *.gz 32 | *.sql 33 | *.sqlite3 34 | socket 35 | -------------------------------------------------------------------------------- /zapisy/scripts/timebonus.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ObjectDoesNotExist 2 | from zapisy.apps.users.models import Student 3 | timebonusfile = 'timebonus2016.txt' 4 | 5 | 6 | def process(line): 7 | line = line.strip() 8 | indeks, bonus = line.split(',') 9 | try: 10 | student = Student.objects.get(matricula=indeks) 11 | student.records_opening_bonus_minutes = bonus 12 | student.save() 13 | except ObjectDoesNotExist: 14 | print(indeks + ': not found') 15 | 16 | 17 | def run(): 18 | file = open(timebonusfile) 19 | for line in file: 20 | process(line) 21 | -------------------------------------------------------------------------------- /zapisy/apps/news/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.db import models 3 | from pagedown.widgets import AdminPagedownWidget 4 | 5 | from apps.news.models import News 6 | 7 | 8 | @admin.register(News) 9 | class NewsAdmin(admin.ModelAdmin): 10 | fields = ('title', 'body', 'author', 'priority') 11 | list_display = ('title', 'date') 12 | list_filter = ['date'] 13 | formfield_overrides = { 14 | models.TextField: { 15 | 'widget': AdminPagedownWidget 16 | }, 17 | } 18 | 19 | def get_changeform_initial_data(self, request): 20 | return {'author': request.user} 21 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0008_auto_20180525_2259.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-05-25 22:59 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('courses', '0007_auto_20180525_0559'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='courseentity', 17 | name='in_prefs', 18 | field=models.BooleanField(default=False, verbose_name='w preferencjach'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /infra/hosts/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure("2") do |config| 5 | config.vm.box = "ubuntu/focal64" 6 | 7 | # Create a private network, which allows host-only access to the machine 8 | # using a specific IP. 9 | config.vm.network "private_network", ip: "192.168.33.10" 10 | 11 | config.vm.provider "virtualbox" do |vb| 12 | # Fix Ubuntu Focal box issue: https://bugs.launchpad.net/cloud-images/+bug/1829625 13 | vb.customize [ "modifyvm", :id, "--uartmode1", "file", File::NULL ] 14 | 15 | # Customize the amount of memory on the VM: 16 | vb.memory = "4096" 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /zapisy/apps/statistics/assets/statistics-widget.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import StatisticsList from "./components/StatisticsList.vue"; 3 | import StatisticsFilter from "./components/StatisticsFilter.vue"; 4 | import store from "./store"; 5 | 6 | new Vue({ 7 | el: "#statistics-filter", 8 | components: { 9 | StatisticsFilter, 10 | }, 11 | render: function (h) { 12 | return h(StatisticsFilter); 13 | }, 14 | store, 15 | }); 16 | 17 | new Vue({ 18 | el: "#statistics-list", 19 | components: { 20 | StatisticsList, 21 | }, 22 | render: function (h) { 23 | return h(StatisticsList); 24 | }, 25 | store, 26 | }); 27 | -------------------------------------------------------------------------------- /zapisy/apps/offer/desiderata/migrations/0003_auto_20171112_2306.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('desiderata', '0002_auto_20170529_1617'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name='desiderata', 13 | name='day', 14 | field=models.CharField(max_length=1, verbose_name=b'dzie\xc5\x84 tygodnia', choices=[(b'1', 'poniedzia\u0142ek'), (b'2', 'wtorek'), (b'3', '\u015broda'), (b'4', 'czwartek'), (b'5', 'pi\u0105tek'), (b'6', 'sobota'), (b'7', 'niedziela')]), 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0012_auto_20190206_2301.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.8 on 2019-02-06 23:01 2 | 3 | import django.core.validators 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0011_create_external_contractors_group'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='employee', 16 | name='consultations', 17 | field=models.TextField(blank=True, null=True, validators=[django.core.validators.MaxLengthValidator(4200)], verbose_name='konsultacje'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/assets/course-list-index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | 4 | import CourseList from "./components/CourseList.vue"; 5 | import CourseFilter from "../../timetable/assets/components/CourseFilter.vue"; 6 | import filters from "../../timetable/assets/store/filters"; 7 | 8 | Vue.use(Vuex); 9 | 10 | const store = new Vuex.Store({ 11 | modules: { 12 | filters, 13 | }, 14 | }); 15 | 16 | if (document.getElementById("course-filter") !== null) { 17 | new Vue({ el: "#course-filter", render: (h) => h(CourseFilter), store }); 18 | } 19 | 20 | new Vue({ el: "#course-list", render: (h) => h(CourseList), store }); 21 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/migrations/0008_notificationpreferencesteacher_thesis_has_been_accepted.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.14 on 2024-02-06 16:43 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('notifications', '0007_auto_20201010_1607'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='notificationpreferencesteacher', 15 | name='thesis_has_been_accepted', 16 | field=models.BooleanField(default=True, verbose_name='Powiadomienie o akceptacji tematu pracy dyplomowej'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/offer/assignments/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.plan_view, name='assignments-view'), 7 | path('wizard/', views.assignments_wizard, name='assignments-wizard'), 8 | path('wizard/assignments', views.create_assignments_sheet, name='create-assignments-sheet'), 9 | # create voting results sheet 10 | path('wizard/voting', views.create_voting_sheet, name='create-voting-sheet'), 11 | # generate scheduler file 12 | path('wizard/scheduler//', 13 | views.generate_scheduler_file, 14 | name='generate-scheduler-file'), 15 | ] 16 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/migrations/0005_auto_20171112_2306.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('schedule', '0004_auto_20170606_1842'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name='specialreservation', 13 | name='dayOfWeek', 14 | field=models.CharField(max_length=1, verbose_name=b'dzie\xc5\x84 tygodnia', choices=[(b'1', 'poniedzia\u0142ek'), (b'2', 'wtorek'), (b'3', '\u015broda'), (b'4', 'czwartek'), (b'5', 'pi\u0105tek'), (b'6', 'sobota'), (b'7', 'niedziela')]), 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/templatetags/proposal_status.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | 3 | from apps.offer.proposal.models import ProposalStatus, SemesterChoices 4 | 5 | register = template.Library() 6 | 7 | 8 | @register.filter 9 | def status_label(status: int) -> str: 10 | """Returns a lowercase label (const identifier) for a given status code.""" 11 | status = ProposalStatus(status) 12 | return status._name_.lower() 13 | 14 | 15 | @register.filter 16 | def semester_display(semester: SemesterChoices) -> str: 17 | """Returns a display value of a SemesterChoices enum.""" 18 | semester = SemesterChoices(semester) 19 | return semester.label 20 | -------------------------------------------------------------------------------- /zapisy/mailer/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from mailer.models import Message, DontSendEntry, MessageLog 3 | 4 | 5 | class MessageAdmin(admin.ModelAdmin): 6 | list_display = ('id', 'to_address', 'subject', 'when_added', 'priority') 7 | 8 | 9 | class DontSendEntryAdmin(admin.ModelAdmin): 10 | list_display = ('to_address', 'when_added') 11 | 12 | 13 | class MessageLogAdmin(admin.ModelAdmin): 14 | list_display = ('id', 'to_address', 'subject', 'when_attempted', 'result') 15 | 16 | 17 | admin.site.register(Message, MessageAdmin) 18 | admin.site.register(DontSendEntry, DontSendEntryAdmin) 19 | admin.site.register(MessageLog, MessageLogAdmin) 20 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/timetable/assets/store/index.ts: -------------------------------------------------------------------------------- 1 | // This module implements a state of the prototype. 2 | // 3 | // The main purpose of the state is to know, which groups should be presented on 4 | // the timetable, and what their status is. It will however also maintain the 5 | // collections of downloaded groups that are not currently presented. 6 | import Vue from "vue"; 7 | import Vuex from "vuex"; 8 | 9 | import groups from "./groups"; 10 | import courses from "./courses"; 11 | import filters from "./filters"; 12 | 13 | Vue.use(Vuex); 14 | 15 | export default new Vuex.Store({ 16 | modules: { 17 | groups, 18 | courses, 19 | filters, 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /zapisy/apps/statistics/templates/statistics/groups_list.html: -------------------------------------------------------------------------------- 1 | {% extends "statistics/base.html" %} 2 | {% load render_bundle from webpack_loader %} 3 | 4 | {% load filters %} 5 | {% load course_types %} 6 | 7 | {% block statistics-groups-active %}active{% endblock %} 8 | 9 | {% block statistics-content %} 10 | 11 | {{ courses_list|json_script:"statistics-data" }} 12 | 13 |
14 |
15 |

16 |
17 |
18 | {% endblock %} 19 | 20 | {% block rendered_bundles %} 21 | {% render_bundle 'statistics-widget' %} 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/migrations/0007_record_priority.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.8 on 2018-12-08 19:54 2 | 3 | import django.core.validators 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('records', '0006_auto_20180813_2243'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='record', 16 | name='priority', 17 | field=models.IntegerField(default=5, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(10)], verbose_name='priorytet'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/migrations/0007_auto_20190529_1419.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-05-29 14:19 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('poll', '0006_auto_20190804_1129'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='template', 15 | name='course', 16 | ), 17 | migrations.AlterField( 18 | model_name='poll', 19 | name='share_result', 20 | field=models.BooleanField(blank=True, default=False, verbose_name='udostępnij wyniki'), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/migrations/0009_auto_20201029_1344.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.2 on 2020-10-29 13:44 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('poll', '0008_auto_20200129_1846'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='schema', 15 | name='questions', 16 | field=models.JSONField(default=dict), 17 | ), 18 | migrations.AlterField( 19 | model_name='submission', 20 | name='answers', 21 | field=models.JSONField(default=dict), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /zapisy/apps/news/tests/utils.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | from random import randint 3 | 4 | from apps.news.models import News, PriorityChoices 5 | from apps.users.tests.factories import UserFactory 6 | 7 | 8 | def generate_random_news(new=randint(1, 6), published=True): 9 | ns = [] 10 | author_user = UserFactory() 11 | for i in range(new): 12 | n = News(title="news", body="news body", 13 | date=datetime.now() - timedelta(days=randint(0, 6)), 14 | author_id=author_user.pk) 15 | if not published: 16 | n.priority = PriorityChoices.HIDDEN 17 | n.save() 18 | ns.append(n) 19 | return ns 20 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/assets/sortable-table.js: -------------------------------------------------------------------------------- 1 | import * as $ from "jquery"; 2 | import "tablesorter"; 3 | 4 | $(function () { 5 | const collator = new Intl.Collator("pl"); 6 | $(".table").tablesorter({ 7 | // sort first column in ascending order 8 | sortList: [[0, 0]], 9 | // set up proper sorting of words containing polish letters 10 | textSorter: function (a, b) { 11 | return collator.compare(a, b); 12 | }, 13 | // set up icons indicating sorting order 14 | headerTemplate: "{content}{icon}", 15 | cssIcon: "tablesorter-icon", 16 | cssIconAsc: "fa fa-sort-up", 17 | cssIconDesc: "fa fa-sort-down", 18 | cssIconNone: "fa fa-sort", 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/models/effects.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Effects(models.Model): 5 | group_name = models.CharField(max_length=250, verbose_name='grupa efektów') 6 | description = models.TextField(verbose_name='opis', null=True, blank=True) 7 | 8 | class Meta: 9 | verbose_name = 'Grupa Efektów' 10 | verbose_name_plural = 'Grupy Efektów' 11 | app_label = 'courses' 12 | 13 | def __str__(self): 14 | return self.group_name 15 | 16 | def serialize_for_json(self): 17 | return { 18 | 'id': self.pk, 19 | 'name': self.group_name, 20 | 'description': self.description 21 | } 22 | -------------------------------------------------------------------------------- /zapisy/apps/news/migrations/0002_auto_20180525_0559.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-05-25 05:59 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('news', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='news', 17 | name='category', 18 | field=models.CharField(choices=[('-', 'Hidden'), ('offer', 'Oferta'), ('enrollment', 'Zapisy'), ('grade', 'Ocena zajęć')], default='-', max_length=15, verbose_name='Kategoria'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/migrations/0006_notificationpreferencesteacher_thesis_voting_has_been_activated.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.13 on 2020-02-27 02:24 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('notifications', '0005_auto_20190929_1436'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='notificationpreferencesteacher', 15 | name='thesis_voting_has_been_activated', 16 | field=models.BooleanField(default=True, verbose_name='Powiadomienie o głosowaniu na pracę dyplomową (dotyczy Komisji Prac Dyplomowych)'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/offer/preferences/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib import messages 2 | from django.shortcuts import render 3 | 4 | from apps.users.decorators import employee_required 5 | 6 | from .forms import prepare_formset 7 | 8 | 9 | @employee_required 10 | def main(request): 11 | employee = request.user.employee 12 | 13 | if request.method == 'POST': 14 | formset = prepare_formset(employee, post=request.POST) 15 | if formset.is_valid(): 16 | formset.save() 17 | messages.info(request, "Zapisano głos.") 18 | else: 19 | formset = prepare_formset(employee) 20 | 21 | return render(request, 'preferences/main.html', { 22 | 'formset': formset, 23 | }) 24 | -------------------------------------------------------------------------------- /zapisy/mailer/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Various utils. 3 | """ 4 | 5 | from mailer import send_html_mail 6 | from django.template import Context 7 | from django.template.loader import get_template 8 | 9 | 10 | MASS_MAIL_FROM = 'zapisy@cs.uni.wroc.pl' 11 | 12 | 13 | def render_and_send_email( 14 | subject, 15 | template, 16 | template_html, 17 | data, 18 | recipient_list, 19 | from_email=MASS_MAIL_FROM): 20 | 21 | con = Context(data) 22 | tem = get_template(template) 23 | plaintext_body = tem.render(con) 24 | tem = get_template(template_html) 25 | html_body = tem.render(con) 26 | 27 | send_html_mail(subject, plaintext_body, html_body, from_email, recipient_list) 28 | -------------------------------------------------------------------------------- /zapisy/apps/schedulersync/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import CourseMap, EmployeeMap 4 | 5 | 6 | class CourseMapAdmin(admin.ModelAdmin): 7 | list_display = ('scheduler_course', 'proposal') 8 | search_fields = ('scheduler_course', 'proposal__name', 'proposal__name_en') 9 | ordering = ('scheduler_course',) 10 | 11 | 12 | class EmployeeMapAdmin(admin.ModelAdmin): 13 | list_display = ('scheduler_username', 'employee') 14 | search_fields = ('scheduler_username', 'employee__user__first_name', 'employee__user__last_name') 15 | ordering = ('scheduler_username',) 16 | 17 | 18 | admin.site.register(EmployeeMap, EmployeeMapAdmin) 19 | admin.site.register(CourseMap, CourseMapAdmin) 20 | -------------------------------------------------------------------------------- /zapisy/apps/users/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib.auth.models import User 3 | 4 | from .models import Employee 5 | 6 | 7 | class EmailChangeForm(forms.ModelForm): 8 | class Meta: 9 | model = User 10 | fields = ['email'] 11 | 12 | def clean_email(self): 13 | email = self.cleaned_data['email'] 14 | if User.objects.exclude(pk=self.instance.pk).filter(email=email).exists(): 15 | raise forms.ValidationError("Adres jest już użyty przez innego użytkownika") 16 | return email 17 | 18 | 19 | class EmployeeDataForm(forms.ModelForm): 20 | class Meta: 21 | model = Employee 22 | fields = ('title', 'room', 'homepage', 'consultations',) 23 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/migrations/0010_auto_20220801_2026.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.14 on 2022-08-01 20:26 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('courses', '0036_auto_20211022_1641'), 11 | ('schedule', '0009_term_ignore_conflicts'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='specialreservation', 17 | name='classroom', 18 | field=models.ForeignKey(limit_choices_to={'can_reserve': True}, on_delete=django.db.models.deletion.CASCADE, to='courses.classroom', verbose_name='sala'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/templates/proposal/fields/collapsable-fieldset.html: -------------------------------------------------------------------------------- 1 |
4 | 5 | {% if legend %}{{ legend|safe }}{% endif %} 6 | 10 | 11 |
12 | {{ fields|safe }} 13 |
14 |
15 | -------------------------------------------------------------------------------- /zapisy/apps/theses/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path("", views.list_all, name="main"), 7 | path("", views.view_thesis, name="selected_thesis"), 8 | path('/edit', views.edit_thesis, name='edit_thesis'), 9 | path("new", views.new_thesis, name="new_thesis"), 10 | path('/vote', views.vote_for_thesis, name="vote_thesis"), 11 | path('/remark', views.edit_remark, name="remark_thesis"), 12 | path('/form/', views.gen_form, name="gen_form"), 13 | path('/rejecter', views.rejecter_decision, name="rejecter_thesis"), 14 | path('/delete', views.delete_thesis, name="delete_thesis"), 15 | ] 16 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/timetable/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from apps.enrollment.timetable import views 4 | 5 | urlpatterns = [ 6 | path('', views.my_timetable, name='my-timetable'), 7 | path('semester//', views.my_timetable, name='my-timetable-semester'), 8 | path('prototype/', views.my_prototype, name='my-prototype'), 9 | path('prototype/action//', views.prototype_action, name='prototype-action'), 10 | path('prototype/course//', views.prototype_get_course, name='prototype-get-course'), 11 | path('prototype/update/', views.prototype_update_groups, name='prototype-update'), 12 | path('calendar-export/', views.calendar_export, name='calendar-export') 13 | ] 14 | -------------------------------------------------------------------------------- /zapisy/scripts/reassign_turns.py: -------------------------------------------------------------------------------- 1 | from apps.users.models import Student 2 | from django.contrib.auth.models import User, Group 3 | import random 4 | 5 | turns = ['tura1', 'tura2', 'tura3'] 6 | 7 | 8 | def run(): 9 | groups = Group.objects.filter(name__in=turns) 10 | # clear groups 11 | for group in groups: 12 | group.user_set.clear() 13 | # find all freshmen and shuffle the list 14 | students = list(Student.objects.filter( 15 | status=0, 16 | semestr=1, 17 | program__in=[4, 12, 14])) 18 | random.shuffle(students) 19 | # assign students to groups 20 | for i, student in enumerate(students): 21 | group = groups[i % groups.count()] 22 | group.user_set.add(student.user) 23 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0025_type_obligatory.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-06-30 11:21 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0024_auto_20190603_0028'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='type', 15 | name='obligatory', 16 | field=models.BooleanField(default=False, verbose_name='przedmioty obowiązkowe'), 17 | ), 18 | migrations.RunSQL( 19 | sql='''UPDATE courses_type SET obligatory=True WHERE short_name ILIKE 'O%';''', 20 | reverse_sql=migrations.RunSQL.noop, 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/timetable/assets/prototype-legend.scss: -------------------------------------------------------------------------------- 1 | // Styles to apply to the legend displayed below the timetable prototype 2 | // component. 3 | 4 | ul.schedule-legend { 5 | list-style: none; 6 | 7 | .legend-box { 8 | display: inline-block; 9 | height: 18px; 10 | width: 18px; 11 | vertical-align: middle; 12 | margin-right: 6px; 13 | 14 | // Colours are copied from `components/Term.vue`. 15 | &.enrolled { 16 | background: #e0eeee; 17 | } 18 | &.enqueued { 19 | background: #f5e5c4; 20 | } 21 | &.pinned { 22 | background: #fae8f8; 23 | } 24 | } 25 | 26 | // Colours will be presented in divs, icons in spans. 27 | div.legend-box { 28 | border: 1px solid; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /zapisy/templates/500.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | Błąd 500 – System Zapisów 8 | 9 | 10 | 11 |

Błąd 500

12 |

Wystąpił błąd w naszym oprogramowaniu.

13 |

Szczegóły błędu zostały zapisane w logu serwisu, a administratorzy zostali powiadomieni, ale jeżeli posiadasz 14 | dodatkowe informacje, które pomogą namierzyć przyczynę problemu, 15 | stwórz nowy problem na trackerze Zapisów. 16 |

17 |

Powrót do strony głównej.

18 | 19 | 20 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/fixtures/users.yaml: -------------------------------------------------------------------------------- 1 | # An example student 2 | - model: auth.user 3 | pk: 1 4 | fields: 5 | username: jdz 6 | first_name: Jan 7 | last_name: Dzban 8 | is_active: true 9 | 10 | - model: users.student 11 | pk: 1 12 | fields: 13 | matricula: 221135 14 | user: 1 15 | 16 | # An example employee 17 | - model: auth.user 18 | pk: 2 19 | fields: 20 | username: pr4c0wn1k 21 | is_active: true 22 | 23 | - model: users.employee 24 | pk: 2 25 | fields: 26 | user: 2 27 | 28 | # Another example employee 29 | - model: auth.user 30 | pk: 3 31 | fields: 32 | username: pr4c0wn1kNum3rDwa 33 | is_active: true 34 | 35 | - model: users.employee 36 | pk: 3 37 | fields: 38 | user: 3 39 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.offer, name='offer-main'), 7 | path('add/', views.proposal_edit, name='proposal-form'), 8 | path('teacher/', views.my_proposals, name='my-proposals'), 9 | path('teacher//', views.my_proposals, name='my-proposal-show'), 10 | path('/edit', views.proposal_edit, name='proposal-edit'), 11 | path('/clone', views.proposal_clone, name='proposal-clone'), 12 | path('/delete', views.proposal_delete_draft, name='proposal-delete'), 13 | path('/', views.offer, name='offer-page'), 14 | path('/syllabus', views.syllabus, name='syllabus'), 15 | ] 16 | -------------------------------------------------------------------------------- /zapisy/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'zapisy.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /zapisy/scripts/random_enrollment.py: -------------------------------------------------------------------------------- 1 | from apps.enrollment.records.models.records import Record, RecordStatus 2 | from apps.enrollment.courses.models.group import Group 3 | from apps.users.models import Student 4 | import random 5 | 6 | group_ids = [15694, 15696, 15697] 7 | 8 | 9 | def run(): 10 | # find all freshmen and shuffle the list 11 | students = Student.objects.filter( 12 | status=0, 13 | semestr=1, 14 | program__in=[4, 12]).order_by('?') 15 | # assign students to groups 16 | for i, student in enumerate(students): 17 | group_id = group_ids[i % len(group_ids)] 18 | Record.objects.create( 19 | group_id=group_id, 20 | student=student, 21 | status=RecordStatus.ENROLLED) 22 | -------------------------------------------------------------------------------- /zapisy/apps/grade/ticket_create/models/student_graded.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from apps.enrollment.courses.models.semester import Semester 4 | from apps.users.models import Student 5 | 6 | 7 | class StudentGraded(models.Model): 8 | student = models.ForeignKey(Student, on_delete=models.CASCADE, verbose_name="student") 9 | semester = models.ForeignKey(Semester, on_delete=models.CASCADE, verbose_name="semestr") 10 | 11 | class Meta: 12 | verbose_name = "udział w ocenie" 13 | verbose_name_plural = "udział w ocenie" 14 | unique_together = [['student', 'semester']] 15 | app_label = 'ticket_create' 16 | 17 | def __str__(self): 18 | return f"{self.student} wygenerował bilety oceny w semestrze {self.semester}" 19 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0020_auto_20190721_1315.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-07-21 13:15 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0019_auto_20190508_1348'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='group', 15 | name='type', 16 | field=models.CharField(choices=[('1', 'wykład'), ('2', 'ćwiczenia'), ('3', 'pracownia'), ('5', 'ćwiczenio-pracownia'), ('6', 'seminarium'), ('7', 'lektorat'), ('8', 'WF'), ('9', 'repetytorium'), ('10', 'projekt'), ('11', 'tutoring'), ('12', 'proseminarium')], max_length=2, verbose_name='typ zajęć'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/offer/preferences/migrations/0007_auto_20201106_2039.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.3 on 2020-11-06 20:39 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('preferences', '0006_auto_20190804_1129'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='preferencesquestion', 15 | name='class_type', 16 | field=models.CharField(choices=[(1, 'wykład'), (2, 'ćwiczenia'), (3, 'pracownia'), (5, 'ćwiczenio-pracownia'), (6, 'seminarium'), (7, 'lektorat'), (8, 'WF'), (9, 'repetytorium'), (10, 'projekt'), (11, 'tutoring'), (12, 'proseminarium')], max_length=2, verbose_name='typ zajęć'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from apps.notifications.exceptions import DescriptionArgumentMissingException 4 | from apps.notifications.exceptions import TitleArgumentMissingException 5 | from apps.notifications.templates import mapping 6 | from apps.notifications.templates import mapping_title 7 | 8 | 9 | def render_description(description_id: str, description_args: Dict): 10 | try: 11 | return mapping[description_id].format(**description_args) 12 | except KeyError: 13 | raise DescriptionArgumentMissingException 14 | 15 | 16 | def render_title(title_id: str, title_args: Dict): 17 | try: 18 | return mapping_title[title_id].format(**title_args) 19 | except KeyError: 20 | raise TitleArgumentMissingException 21 | -------------------------------------------------------------------------------- /zapisy/templates/endless/show_pages.html: -------------------------------------------------------------------------------- 1 | 20 | 21 | -------------------------------------------------------------------------------- /env/.env_ci: -------------------------------------------------------------------------------- 1 | DEBUG=True 2 | RELEASE=False 3 | ALLOWED_HOSTS=* 4 | TEMPLATE_DEBUG=True 5 | DATABASE_NAME=fereol_test 6 | DATABASE_USER=postgres 7 | DATABASE_PASSWORD=fereolpass 8 | DATABASE_PORT=5432 9 | DEBUG_TOOLBAR_ALLOWED_USERS=209067,209138,208934,gosia,stanislaw 10 | EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend 11 | 12 | VOTING_RESULTS_SPREADSHEET_ID=placeholder 13 | CLASS_ASSIGNMENT_SPREADSHEET_ID=placeholder 14 | 15 | GDRIVE_SERVICE_TYPE=placeholder 16 | GDRIVE_PROJECT_ID=placeholder 17 | GDRIVE_PRIVATE_KEY_ID=placeholder 18 | 19 | GDRIVE_PRIVATE_KEY=placeholder 20 | GDRIVE_CLIENT_EMAIL=placeholder 21 | GDRIVE_CLIENT_ID=placeholder 22 | GDRIVE_AUTH_URI=placeholder 23 | GDRIVE_TOKEN_URI=placeholder 24 | GDRIVE_AUTH_PROVIDER=placeholder 25 | GDRIVE_CLIENT_CERT_URL=placeholder 26 | -------------------------------------------------------------------------------- /zapisy/mailer/management/commands/send_mail.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from django.conf import settings 4 | from django.core.management.base import BaseCommand 5 | 6 | from mailer.engine import send_all 7 | 8 | logger = logging.getLogger('mailer.send_mail') 9 | 10 | # allow a sysadmin to pause the sending of mail temporarily. 11 | PAUSE_SEND = getattr(settings, "MAILER_PAUSE_SEND", False) 12 | 13 | 14 | class Command(BaseCommand): 15 | help = 'Do one pass through the mail queue, attempting to send all mail.' 16 | 17 | def handle(self, *args, **options): 18 | logger.info("-" * 72) 19 | # if PAUSE_SEND is turned on don't do anything. 20 | if not PAUSE_SEND: 21 | send_all() 22 | else: 23 | logger.info("sending is paused, quitting.") 24 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/migrations/0006_auto_20190804_1129.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-08-04 11:29 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('poll', '0005_auto_20180525_0559'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='template', 15 | name='group_type', 16 | field=models.CharField(blank=True, choices=[('1', 'wykład'), ('2', 'ćwiczenia'), ('3', 'pracownia'), ('5', 'ćwiczenio-pracownia'), ('6', 'seminarium'), ('7', 'lektorat'), ('8', 'WF'), ('9', 'repetytorium'), ('10', 'projekt'), ('11', 'tutoring'), ('12', 'proseminarium')], max_length=2, null=True, verbose_name='typ zajęć'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/migrations/0002_auto_20180525_0559.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-05-25 05:59 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('notifications', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='notificationpreferences', 17 | name='type', 18 | field=models.CharField(choices=[('send-news', 'Dodano nowy news'), ('enrollment-limit', 'Podniesiono limit ECTS'), ('vote-start', 'Rozpoczęło się głosowanie'), ('grade-start', 'Rozpoczęła się ocena')], max_length=50, verbose_name='typ'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /zapisy/apps/offer/preferences/migrations/0006_auto_20190804_1129.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-08-04 11:29 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('preferences', '0005_auto_20190511_1814'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='preferencesquestion', 15 | name='class_type', 16 | field=models.CharField(choices=[('1', 'wykład'), ('2', 'ćwiczenia'), ('3', 'pracownia'), ('5', 'ćwiczenio-pracownia'), ('6', 'seminarium'), ('7', 'lektorat'), ('8', 'WF'), ('9', 'repetytorium'), ('10', 'projekt'), ('11', 'tutoring'), ('12', 'proseminarium')], max_length=2, verbose_name='typ zajęć'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /infra/playbooks/update_ssl.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: deploy 3 | 4 | vars: 5 | ansible_python_interpreter: "/usr/bin/python3" 6 | 7 | tasks: 8 | - name: Deploy new OpenSSL private key 9 | copy: 10 | src: ssl/zapisy.key 11 | dest: "/etc/ssl/private/{{ deploy_server_name }}.key" 12 | force: yes 13 | group: ssl-cert 14 | mode: 0640 15 | become: yes 16 | 17 | - name: Deploy new OpenSSL certificate 18 | copy: 19 | src: ssl/zapisy.crt 20 | dest: "/etc/ssl/certs/{{ deploy_server_name }}.crt" 21 | force: yes 22 | mode: 0644 23 | become: yes 24 | 25 | - name: Restart nginx service 26 | service: 27 | name: nginx 28 | enabled: yes 29 | state: restarted 30 | become: yes 31 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/assets/sortable-table.scss: -------------------------------------------------------------------------------- 1 | @import "~bootstrap/scss/functions"; 2 | @import "~bootstrap/scss/variables"; 3 | @import "~bootstrap/scss/mixins"; 4 | 5 | td:first-child { 6 | width: 60%; 7 | } 8 | 9 | th.tablesorter-header:not(.sorter-false) { 10 | cursor: pointer; 11 | 12 | &:hover { 13 | background-color: rgba(0, 0, 0, 0.075); 14 | } 15 | 16 | .tablesorter-header-inner { 17 | display: flex; 18 | justify-content: center; 19 | align-items: center; 20 | 21 | .tablesorter-icon { 22 | color: #bfbfc1; 23 | margin-left: 0.75rem; 24 | 25 | @include media-breakpoint-down(md) { 26 | display: none; 27 | } 28 | } 29 | } 30 | 31 | &:first-child .tablesorter-header-inner { 32 | justify-content: flex-start; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/tests/semester_year_provider.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from faker import Faker 4 | from faker.providers import BaseProvider 5 | 6 | from apps.enrollment.courses.models.semester import Semester 7 | 8 | SEMESTER_YEAR_RANGE = 50 9 | 10 | 11 | class SemesterYearProvider(BaseProvider): 12 | def semester_year(self): 13 | start_year = datetime.now().year 14 | end_year = start_year + SEMESTER_YEAR_RANGE 15 | f = Faker() 16 | while True: 17 | year = f.random_int(start_year, end_year) 18 | try: 19 | semester_year = Semester.get_semester_year_from_raw_year(year) 20 | Semester.objects.get(year=semester_year) 21 | except Semester.DoesNotExist: 22 | return year 23 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/timetable/migrations/0004_auto_20190822_1346.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.9 on 2019-08-22 13:46 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('timetable', '0003_auto_20190215_0909'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterUniqueTogether( 14 | name='hiddengroups', 15 | unique_together=set(), 16 | ), 17 | migrations.RemoveField( 18 | model_name='hiddengroups', 19 | name='group', 20 | ), 21 | migrations.RemoveField( 22 | model_name='hiddengroups', 23 | name='role', 24 | ), 25 | migrations.DeleteModel( 26 | name='HiddenGroups', 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /zapisy/apps/offer/preferences/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Preference, PreferencesQuestion 4 | 5 | 6 | @admin.register(PreferencesQuestion) 7 | class PreferencesQuestionAdmin(admin.ModelAdmin): 8 | autocomplete_fields = ['proposal'] 9 | 10 | 11 | @admin.register(Preference) 12 | class PreferenceAdmin(admin.ModelAdmin): 13 | list_display = ('employee', 'proposal', 'class_type', 'answer') 14 | list_filter = (('employee', admin.RelatedOnlyFieldListFilter), 15 | ('question__proposal', 16 | admin.RelatedOnlyFieldListFilter), 'question__class_type', 'answer') 17 | 18 | def proposal(self, obj): 19 | return obj.question.proposal 20 | 21 | def class_type(self, obj): 22 | return obj.question.get_class_type_display() 23 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/migrations/0006_auto_20190812_1025.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-08-12 10:25 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('vote', '0005_remove_singlevote_entity'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RunSQL( 15 | sql='''DELETE FROM vote_singlevote WHERE proposal_id IS NULL;''', 16 | reverse_sql=migrations.RunSQL.noop, 17 | ), 18 | migrations.AlterField( 19 | model_name='singlevote', 20 | name='proposal', 21 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='proposal.Proposal', verbose_name='propozycja'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/models/tag.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Tag(models.Model): 5 | short_name = models.CharField(max_length=50, verbose_name='nazwa skrócona') 6 | full_name = models.CharField(max_length=250, verbose_name='nazwa pełna') 7 | description = models.TextField(verbose_name='opis') 8 | 9 | class Meta: 10 | verbose_name = 'Tag' 11 | verbose_name_plural = 'Tagi' 12 | app_label = 'courses' 13 | 14 | def __str__(self): 15 | return str(self.short_name) + ' (' + str(self.full_name) + ')' 16 | 17 | def serialize_for_json(self): 18 | return { 19 | 'id': self.pk, 20 | 'short_name': self.short_name, 21 | 'full_name': self.full_name, 22 | 'description': self.description 23 | } 24 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/migrations/0009_auto_20190109_1236.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.8 on 2019-01-09 12:36 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0010_auto_20180804_2031'), 10 | ('users', '0012_auto_20180804_2031'), 11 | ('records', '0008_programgrouprestrictions'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterUniqueTogether( 16 | name='programgrouprestrictions', 17 | unique_together={('group', 'program')}, 18 | ), 19 | migrations.AddIndex( 20 | model_name='programgrouprestrictions', 21 | index=models.Index(fields=['group', 'program'], name='records_pro_group_i_802ae5_idx'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /zapisy/apps/api/rest/v1/api_wrapper/setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="sz_api", 8 | version="1.0.0", 9 | author="Mariusz Bielecki", 10 | author_email="maniekb12@gmail.com", 11 | description="API wrapper for https://zapisy.ii.uni.wroc.pl/api/v1", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/iiuni/projektzapisy/", 15 | packages=setuptools.find_packages(), 16 | classifiers=[ 17 | "Programming Language :: Python :: 3", 18 | "License :: OSI Approved :: MIT License", 19 | "Operating System :: OS Independent", 20 | ], 21 | install_requires=["requests"], 22 | python_requires='>=3.6', 23 | ) 24 | -------------------------------------------------------------------------------- /infra/playbooks/templates/nginx.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=A high performance web server and a reverse proxy server 3 | After=network.target 4 | 5 | [Service] 6 | Type=forking 7 | PIDFile=/run/nginx/nginx.pid 8 | ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;' 9 | ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;' 10 | ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload 11 | ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx/nginx.pid 12 | TimeoutStopSec=5 13 | KillMode=mixed 14 | SystemCallErrorNumber=EPERM 15 | LockPersonality=true 16 | NoNewPrivileges=true 17 | Group=www-data 18 | User=www-data 19 | AmbientCapabilities=CAP_NET_BIND_SERVICE 20 | RuntimeDirectory=nginx 21 | Restart=on-failure 22 | RestartSec=10 23 | 24 | [Install] 25 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0036_auto_20211022_1641.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.13 on 2021-10-22 16:41 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0035_auto_20210130_0042'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='term', 15 | name='usos_term_id', 16 | field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Kod DZ_TERMINY w systemie USOS'), 17 | ), 18 | migrations.AlterField( 19 | model_name='term', 20 | name='usos_id', 21 | field=models.PositiveIntegerField(blank=True, null=True, unique=True, verbose_name='Kod DZ_TERMINY_GRUP w systemie USOS'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /infra/playbooks/templates/gunicorn.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Zapisy gunicorn daemon 3 | After=network.target 4 | 5 | [Service] 6 | Restart=always 7 | Type=simple 8 | User={{ deploy_user }} 9 | Group=www-data 10 | WorkingDirectory=/home/{{ deploy_user }}/deploy/current/zapisy 11 | Environment=NEW_RELIC_ENVIRONMENT={{ deploy_env }} 12 | Environment=NEW_RELIC_CONFIG_FILE=/home/{{ deploy_user }}/newrelic.ini 13 | ExecStart=/home/{{ deploy_user }}/deploy/current/venv/bin/newrelic-admin run-program \ 14 | /home/{{ deploy_user }}/deploy/current/venv/bin/gunicorn \ 15 | --name "zapisy" \ 16 | --workers {{ nproc_out.stdout|int * 2 + 1 }} \ 17 | --bind=unix:/home/{{ deploy_user }}/deploy/current/venv/run/gunicorn.sock zapisy.wsgi:application \ 18 | --log-level=debug \ 19 | --log-file=- 20 | 21 | [Install] 22 | WantedBy=multi-user.target 23 | -------------------------------------------------------------------------------- /infra/db_backups/anonymize.sql: -------------------------------------------------------------------------------- 1 | -- Nuke IBANs 2 | UPDATE users_studiazamawiane SET bank_account=''; 3 | 4 | -- Anonymize first/last names 5 | UPDATE auth_user SET first_name=CONCAT(SUBSTRING(first_name, 1, 1), '_', id); 6 | UPDATE auth_user SET last_name=CONCAT(SUBSTRING(last_name, 1, 1), '_', id); 7 | 8 | -- Delete api tokens 9 | DELETE FROM authtoken_token; 10 | 11 | -- Anonymize/remove email addresses 12 | UPDATE auth_user SET email='email@example.org'; 13 | DELETE FROM mailer_message; 14 | DELETE FROM mailer_messagelog; 15 | 16 | -- Make sure everyone has the same simple password, in this case, 'pass' 17 | -- See here: https://docs.djangoproject.com/en/2.0/topics/auth/passwords/ 18 | UPDATE auth_user SET password='pbkdf2_sha256$36000$Z6GlerjZ9cWC$M6zn6XGPc81913R1yw6SMouredUfO/DPnQwZ3XxUCnA='; 19 | 20 | -- Anonymize grade/poll answers 21 | DELETE FROM poll_submission; -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/migrations/0010_auto_20190822_1718.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.9 on 2019-08-22 17:18 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('records', '0009_auto_20190109_1236'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterUniqueTogether( 14 | name='programgrouprestrictions', 15 | unique_together=set(), 16 | ), 17 | migrations.RemoveField( 18 | model_name='programgrouprestrictions', 19 | name='group', 20 | ), 21 | migrations.RemoveField( 22 | model_name='programgrouprestrictions', 23 | name='program', 24 | ), 25 | migrations.DeleteModel( 26 | name='ProgramGroupRestrictions', 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /zapisy/apps/common/assets/main/_variables.scss: -------------------------------------------------------------------------------- 1 | $font-size-base: 0.9rem; 2 | 3 | $display1-size: $font-size-base * 3.5 !default; 4 | $display2-size: $font-size-base * 3 !default; 5 | $display3-size: $font-size-base * 2.5 !default; 6 | $display4-size: $font-size-base * 2 !default; 7 | 8 | $h1-font-size: $font-size-base * 2.25 !default; 9 | $h2-font-size: $font-size-base * 2 !default; 10 | $h3-font-size: $font-size-base * 1.75 !default; 11 | $h4-font-size: $font-size-base * 1.5 !default; 12 | $h5-font-size: $font-size-base * 1.25 !default; 13 | $h6-font-size: $font-size-base !default; 14 | 15 | $enable-responsive-font-sizes: true; 16 | $breadcrumb-bg: #ffffff; 17 | 18 | $input-placeholder-color: #adb5bd; // gray-500 19 | 20 | $primary: rgb(0, 102, 158) !default; 21 | $success: rgb(0, 115, 54) !default; 22 | 23 | $blue: #007bff !default; 24 | $link-color: $blue !default; 25 | -------------------------------------------------------------------------------- /zapisy/apps/common/assets/markdown/render-markdown.scss: -------------------------------------------------------------------------------- 1 | // Scale down headers in course description and syllabus sections. We do that 2 | // because H1 used in the markdown description should not be larger than h2 3 | // before it. 4 | 5 | // We are importing Bootstrap variables to relate our font sizes to those from 6 | // Bootstrap. 7 | @import "~bootstrap/scss/functions"; 8 | @import "~bootstrap/scss/variables"; 9 | @import "~bootstrap/scss/mixins"; 10 | @import "~bootstrap/scss/images"; 11 | 12 | .markdown-rendered { 13 | // H1 in the description must be visibly smaller than H3 outside. 14 | h1 { 15 | font-size: $h3-font-size * 0.75; 16 | } 17 | h2 { 18 | font-size: $h4-font-size * 0.75; 19 | } 20 | h3 { 21 | font-size: $h5-font-size * 0.75; 22 | } 23 | 24 | // Make images responsive 25 | img { 26 | @extend .img-fluid; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0033_auto_20200914_1151.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.10 on 2020-09-14 11:51 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0032_auto_20200908_1240'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='group', 15 | name='auto_enrollment', 16 | field=models.BooleanField(default=False, help_text='Blokuje zapisywanie do grupy. Zamiast tego, studenci są do niej automatycznie zapisani jeśli zapiszą się\xa0do jakiejkolwiek innej grupy z tego przedmiotu. Nie należy blokować\xa0wszystkich grup z przedmiotu ani grup, gdzie studenci powinni mieć wybór (np. jest kilka równoległych grup ćwiczeniowych).', verbose_name='grupa z auto-zapisem'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /zapisy/scripts/single_opening.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from apps.enrollment.courses.models import Group 4 | from apps.enrollment.records.models import GroupOpeningTimes 5 | from apps.users.models import Student 6 | 7 | 8 | def run(): 9 | course_slugs = [ 10 | 'analiza-matematyczna-201920-zimowy', 'kurs-podstawowy-warsztat-informatyka-201920-zimowy', 11 | 'kurs-wstep-do-programowania-w-jezyku-c-201920-zimowy', 12 | 'kurs-wstep-do-programowania-w-jezyku-python-201920-zimowy', 13 | 'wstep-do-informatyki-201920-zimowy' 14 | ] 15 | course_groups = Group.objects.filter(course__slug__in=course_slugs) 16 | 17 | student = Student.objects.get(matricula='314308') 18 | time = datetime.datetime(2019, 10, 1, 15, 0) 19 | 20 | for group in course_groups: 21 | GroupOpeningTimes.objects.create(student=student, group=group, time=time) 22 | -------------------------------------------------------------------------------- /zapisy/apps/common/assets/cookieconsent/display-cookieconsent.ts: -------------------------------------------------------------------------------- 1 | // Displays a EU-required Cookie information bar at the bottom of the screen 2 | // unless the user had already dismissed it. 3 | import "cookieconsent"; 4 | import "cookieconsent/build/cookieconsent.min.css"; 5 | 6 | window.addEventListener("load", function () { 7 | (window as any).cookieconsent.initialise({ 8 | palette: { 9 | popup: { 10 | background: "#222222", 11 | }, 12 | button: { 13 | background: "#00709e", 14 | }, 15 | }, 16 | showLink: false, 17 | theme: "classic", 18 | position: "bottom", 19 | content: { 20 | message: 21 | "System zapisów wykorzystuje pliki cookies. Korzystanie" + 22 | " z witryny oznacza zgodę na ich zapis lub odczyt według" + 23 | " ustawień przeglądarki.", 24 | dismiss: "OK", 25 | }, 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0011_create_external_contractors_group.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | 3 | EXTERNAL_CONTRACTORS_GROUP_NAME = 'external_contractors' 4 | 5 | 6 | def apply_migration(apps, schema_editor): 7 | Group = apps.get_model("auth", "Group") 8 | db_alias = schema_editor.connection.alias 9 | Group.objects.using(db_alias).create(name=EXTERNAL_CONTRACTORS_GROUP_NAME) 10 | 11 | 12 | def revert_migration(apps, schema_editor): 13 | Group = apps.get_model("auth", "Group") 14 | db_alias = schema_editor.connection.alias 15 | Group.objects.using(db_alias).filter(name=EXTERNAL_CONTRACTORS_GROUP_NAME).delete() 16 | 17 | 18 | class Migration(migrations.Migration): 19 | 20 | dependencies = [ 21 | ('users', '0010_auto_20180712_1227'), 22 | ] 23 | 24 | operations = [ 25 | migrations.RunPython(apply_migration, revert_migration) 26 | ] 27 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0012_auto_20180804_2031.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-08-04 20:31 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('users', '0011_create_external_contractors_group'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='openingtimesview', 17 | name='course', 18 | ), 19 | migrations.RemoveField( 20 | model_name='openingtimesview', 21 | name='semester', 22 | ), 23 | migrations.RemoveField( 24 | model_name='openingtimesview', 25 | name='student', 26 | ), 27 | migrations.DeleteModel( 28 | name='OpeningTimesView', 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0010_auto_20180804_2031.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-08-04 20:31 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('courses', '0009_auto_20180712_1227'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='group', 17 | name='enrolled', 18 | ), 19 | migrations.RemoveField( 20 | model_name='group', 21 | name='enrolled_isim', 22 | ), 23 | migrations.RemoveField( 24 | model_name='group', 25 | name='limit_isim', 26 | ), 27 | migrations.RemoveField( 28 | model_name='group', 29 | name='queued', 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /zapisy/apps/statistics/assets/store/filters.ts: -------------------------------------------------------------------------------- 1 | import { every, invokeMap, values } from "lodash"; 2 | 3 | import { CourseInfo } from "./courses"; 4 | 5 | export interface Filter { 6 | visible(c: CourseInfo): boolean; 7 | } 8 | 9 | interface State { 10 | filters: { [id: string]: Filter }; 11 | } 12 | const state: State = { 13 | filters: {}, 14 | }; 15 | 16 | const getters = { 17 | // visible runs all the registered filters on the given course. 18 | visible: (state: State) => (c: CourseInfo) => { 19 | return every(invokeMap(values(state.filters), "visible", c)); 20 | }, 21 | }; 22 | 23 | const mutations = { 24 | // registerFilter can be also used to update filter data. 25 | registerFilter(state: State, { k, f }: { k: string; f: Filter }) { 26 | state.filters[k] = f; 27 | }, 28 | }; 29 | 30 | export default { 31 | namespaced: true, 32 | state, 33 | getters, 34 | mutations, 35 | }; 36 | -------------------------------------------------------------------------------- /zapisy/apps/offer/preferences/migrations/0002_auto_20170529_1617.py: -------------------------------------------------------------------------------- 1 | from django.db import models, migrations 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('preferences', '0001_initial'), 8 | ('users', '0001_initial'), 9 | ('courses', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='preference', 15 | name='employee', 16 | field=models.ForeignKey(verbose_name=b'pracownik', to='users.Employee', on_delete=models.CASCADE), 17 | preserve_default=True, 18 | ), 19 | migrations.AddField( 20 | model_name='preference', 21 | name='proposal', 22 | field=models.ForeignKey(verbose_name=b'propozycja', to='courses.CourseEntity', on_delete=models.CASCADE), 23 | preserve_default=True, 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0035_auto_20210130_0042.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-01-30 00:42 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0034_auto_20201106_2039'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='classroom', 15 | name='order', 16 | ), 17 | migrations.AlterField( 18 | model_name='classroom', 19 | name='usos_id', 20 | field=models.PositiveIntegerField(blank=True, null=True, verbose_name='ID sali w systemie USOS'), 21 | ), 22 | migrations.AlterField( 23 | model_name='group', 24 | name='usos_nr', 25 | field=models.IntegerField(blank=True, null=True, verbose_name='Nr grupy w usos'), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/timetable/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0 on 2018-08-13 22:45 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('courses', '0010_auto_20180804_2031'), 13 | ('users', '0012_auto_20180804_2031'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Pin', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Group')), 22 | ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.Student')), 23 | ], 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /zapisy/apps/theses/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models.signals import post_save 2 | from django.dispatch import receiver 3 | 4 | from apps.notifications.custom_signals import thesis_accepted 5 | from .enums import ThesisVote, ThesisStatus 6 | from .models import Vote 7 | from .system_settings import get_num_required_votes 8 | 9 | 10 | @receiver(post_save, sender=Vote) 11 | def auto_accept(sender, instance: Vote, **kwargs): 12 | """Accepts thesis when enough accepting votes have been submitted.""" 13 | thesis = instance.thesis 14 | vote = instance.vote 15 | if vote == ThesisVote.ACCEPTED and thesis.get_accepted_votes() >= get_num_required_votes(): 16 | if thesis.has_no_students_assigned: 17 | thesis.status = ThesisStatus.ACCEPTED 18 | else: 19 | thesis.status = ThesisStatus.IN_PROGRESS 20 | thesis_accepted.send(sender=Vote, instance=thesis) 21 | thesis.save() 22 | -------------------------------------------------------------------------------- /zapisy/apps/theses/system_settings.py: -------------------------------------------------------------------------------- 1 | """Queries to the Thesis settings singleton. 2 | 3 | The functions in this module permit interaction with the theses system settings 4 | implemented as a single instance of models.ThesesSystemSettings. 5 | """ 6 | from . import models 7 | 8 | 9 | def _get_settings(): 10 | """Get only existing instance of theses system settings object.""" 11 | # There should only be one such object created in migrations 12 | # Deleting it/adding new ones is disabled in the admin, see admin.py 13 | return models.ThesesSystemSettings.objects.get() 14 | 15 | 16 | def get_num_required_votes(): 17 | """How many board members must vote "yes" before a thesis is accepted?""" 18 | return _get_settings().num_required_votes 19 | 20 | 21 | def get_master_rejecter(): 22 | """Get the special board member responsible for rejecting theses.""" 23 | return _get_settings().master_rejecter 24 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0009_auto_20180712_1227.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-07-12 12:27 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('courses', '0008_auto_20180525_2259'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RemoveField( 16 | model_name='group', 17 | name='enrolled_zam', 18 | ), 19 | migrations.RemoveField( 20 | model_name='group', 21 | name='enrolled_zam2012', 22 | ), 23 | migrations.RemoveField( 24 | model_name='group', 25 | name='limit_zamawiane', 26 | ), 27 | migrations.RemoveField( 28 | model_name='group', 29 | name='limit_zamawiane2012', 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /zapisy/apps/common/widgets.py: -------------------------------------------------------------------------------- 1 | from django.forms import Media, widgets 2 | from webpack_loader import utils as webpack_utils 3 | 4 | 5 | class MarkdownArea(widgets.Widget): 6 | """Form widget with live Markdown preview. 7 | 8 | This widget may be used just like any Django widget for any form 9 | (text)field. It automatically adds its assets (css and js) into the form. 10 | They must be included in the template: 11 | 12 | {{ form.media }} 13 | 14 | (Crispy will do it for you.) 15 | """ 16 | template_name = 'widgets/markdown-editor.html' 17 | 18 | @property 19 | def media(self): 20 | js_media = webpack_utils.get_files('common-markdown-editor', extension='js') 21 | css_media = webpack_utils.get_files('common-markdown-editor', extension='css') 22 | return Media(js=[f['url'] for f in js_media], css={ 23 | 'all': [f['url'] for f in css_media], 24 | }) 25 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/migrations/0009_auto_20240705_1517.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.14 on 2024-07-05 15:17 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('notifications', '0008_notificationpreferencesteacher_thesis_has_been_accepted'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='notificationpreferencesstudent', 15 | name='event_decision', 16 | field=models.BooleanField(default=True, verbose_name='Decyzja w sprawie zgłoszonego przez Ciebie wydarzenia'), 17 | ), 18 | migrations.AddField( 19 | model_name='notificationpreferencesteacher', 20 | name='event_decision', 21 | field=models.BooleanField(default=True, verbose_name='Decyzja w sprawie zgłoszonego przez Ciebie wydarzenia'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /zapisy/apps/theses/assets/store/filters.ts: -------------------------------------------------------------------------------- 1 | import { every, invokeMap, values } from "lodash"; 2 | 3 | import { ThesisInfo } from "./theses"; 4 | 5 | export interface Filter { 6 | visible(c: ThesisInfo): boolean; 7 | } 8 | 9 | interface State { 10 | filters: { [id: string]: Filter }; 11 | } 12 | const state: State = { 13 | filters: {}, 14 | }; 15 | 16 | const getters = { 17 | // visible runs all the registered filters on the given course. 18 | visible: (state: State) => (c: ThesisInfo) => { 19 | return every(invokeMap(values(state.filters), "visible", c)); 20 | }, 21 | }; 22 | 23 | const mutations = { 24 | // registerFilter can be also used to update filter data. 25 | registerFilter(state: State, { k, f }: { k: string; f: Filter }) { 26 | state.filters[k] = f; 27 | }, 28 | }; 29 | 30 | const actions = {}; 31 | 32 | export default { 33 | namespaced: true, 34 | state, 35 | getters, 36 | actions, 37 | mutations, 38 | }; 39 | -------------------------------------------------------------------------------- /infra/playbooks/templates/ssl-params.conf: -------------------------------------------------------------------------------- 1 | ssl_protocols TLSv1.2; 2 | ssl_prefer_server_ciphers on; 3 | ssl_dhparam /etc/nginx/dhparam.pem; 4 | ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384; 5 | ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0 6 | ssl_session_timeout 10m; 7 | ssl_session_cache shared:SSL:10m; 8 | ssl_session_tickets off; # Requires nginx >= 1.5.9 9 | ssl_stapling on; # Requires nginx >= 1.3.7 10 | ssl_stapling_verify on; # Requires nginx => 1.3.7 11 | resolver 8.8.8.8 8.8.4.4 valid=300s; 12 | resolver_timeout 5s; 13 | # Disable strict transport security for now. You can uncomment the following 14 | # line if you understand the implications. 15 | # add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; 16 | add_header X-Frame-Options DENY; 17 | add_header X-Content-Type-Options nosniff; 18 | add_header X-XSS-Protection "1; mode=block"; 19 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/timetable/migrations/0003_auto_20190215_0909.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.8 on 2019-02-15 09:09 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0012_auto_20180804_2031'), 10 | ('courses', '0014_auto_20190212_1313'), 11 | ('timetable', '0002_auto_20190112_0200'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RunSQL( 16 | "DELETE FROM timetable_pin a USING timetable_pin b WHERE a.id < b.id AND a.student_id = b.student_id AND a.group_id = b.group_id;" 17 | ), 18 | migrations.AlterUniqueTogether( 19 | name='pin', 20 | unique_together={('student', 'group')}, 21 | ), 22 | migrations.AddIndex( 23 | model_name='pin', 24 | index=models.Index(fields=['student', 'group'], name='timetable_p_student_5e9655_idx'), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /zapisy/apps/users/signals.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import Group 2 | from django.db.models.signals import post_save, pre_save 3 | from django.dispatch import receiver 4 | 5 | from .models import Student 6 | 7 | 8 | @receiver(pre_save, sender=Student) 9 | def unsync_program_with_groups(sender, instance, raw, **kwargs): 10 | if raw: 11 | return 12 | if not instance.pk: 13 | return 14 | s = Student.objects.select_related('program').get(pk=instance.pk) 15 | if s.program is None: 16 | return 17 | g, _ = Group.objects.get_or_create(name=s.program.name) 18 | instance.user.groups.remove(g) 19 | 20 | 21 | @receiver(post_save, sender=Student) 22 | def sync_program_with_groups(sender, instance, created, raw, **kwargs): 23 | if created or raw: 24 | return 25 | if instance.program is None: 26 | return 27 | g, _ = Group.objects.get_or_create(name=instance.program.name) 28 | instance.user.groups.add(g) 29 | -------------------------------------------------------------------------------- /zapisy/scripts/enroll_tutoring_isim.py: -------------------------------------------------------------------------------- 1 | from apps.users.models import Student 2 | from apps.enrollment.courses.models.group import Group 3 | from apps.enrollment.records.models.records import Record, RecordStatus 4 | 5 | 6 | def enroll(group, student): 7 | Record.object.create( 8 | group=group, 9 | student=student, 10 | status=RecordStatus.ENROLLED) 11 | 12 | 13 | def run(): 14 | group_id_first_year = 15699 15 | group_id_rest = 15698 16 | isim_id = 14 17 | g1 = Group.objects.get(id=group_id_first_year) 18 | g2 = Group.objects.get(id=group_id_rest) 19 | sisim = Student.objects.filter(program_id=isim_id) 20 | c1 = 0 21 | c2 = 0 22 | for s in sisim: 23 | if s.ects == 0: 24 | enroll(g1, s) 25 | c1 += 1 26 | else: 27 | enroll(g2, s) 28 | c2 += 1 29 | print("First year isim enrolled: " + str(c1)) 30 | print("Other year isim enrolled: " + str(c2)) 31 | -------------------------------------------------------------------------------- /zapisy/apps/grade/ticket_create/signals.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import RSA 2 | from django.db.models.signals import post_save 3 | from django.dispatch import receiver 4 | 5 | from apps.grade.poll.models import Poll 6 | from apps.grade.ticket_create.models.rsa_keys import RSAKeys 7 | 8 | 9 | @receiver(post_save, sender=Poll) 10 | def save_keys_on_poll_created(sender, instance, created, **kwargs): 11 | if created: 12 | keys = generate_key() 13 | save_keys(instance, keys) 14 | 15 | 16 | def save_keys(poll, keys): 17 | rsa_keys = RSAKeys(poll=poll, private_key=keys[0], public_key=keys[1]) 18 | rsa_keys.save() 19 | 20 | 21 | def generate_key(): 22 | length = 1024 23 | key = RSA.generate(length) 24 | private_key = key.exportKey('PEM').\ 25 | decode(encoding='ascii', errors='strict') 26 | public_key = key.publickey().exportKey('PEM').\ 27 | decode(encoding='ascii', errors='strict') 28 | return (private_key, public_key) 29 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/migrations/0008_programgrouprestrictions.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.8 on 2019-01-08 21:11 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('users', '0012_auto_20180804_2031'), 11 | ('courses', '0010_auto_20180804_2031'), 12 | ('records', '0007_record_priority'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='ProgramGroupRestrictions', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Group')), 21 | ('program', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.Program')), 22 | ], 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/tests/test_serialization.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from django.test import SimpleTestCase 4 | 5 | from apps.notifications.datatypes import Notification 6 | from apps.notifications.serialization import JsonNotificationSerializer 7 | 8 | 9 | class NotificationsSerializationTestCase(SimpleTestCase): 10 | 11 | def test_json_serialization_is_reversible(self): 12 | before = Notification("123456789", datetime(2019, 5, 5, 20, 35, 0, 0), 'aaa{foo}bbb', {'foo': 'bar'}) 13 | serialized = JsonNotificationSerializer().serialize(before) 14 | after = JsonNotificationSerializer().deserialize(serialized) 15 | 16 | self.assertEqual(before.issued_on, after.issued_on) 17 | self.assertEqual(before.target, after.target) 18 | self.assertEqual(before.description_id, after.description_id) 19 | self.assertEqual(before.description_args, after.description_args) 20 | self.assertEqual(before.id, after.id) 21 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/migrations/0008_auto_20190528_1403.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-05-28 14:03 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('proposal', '0007_proposal_semester'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='studentwork', 15 | name='syllabus', 16 | ), 17 | migrations.RemoveField( 18 | model_name='syllabus', 19 | name='entity', 20 | ), 21 | migrations.RemoveField( 22 | model_name='syllabus', 23 | name='learning_methods', 24 | ), 25 | migrations.DeleteModel( 26 | name='LearningMethod', 27 | ), 28 | migrations.DeleteModel( 29 | name='StudentWork', 30 | ), 31 | migrations.DeleteModel( 32 | name='Syllabus', 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /zapisy/apps/theses/templates/theses/list_all.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load render_bundle from webpack_loader %} 3 | 4 | {% block main-subtitle %}System prac dyplomowych{% endblock %} 5 | 6 | {% block theses_active %} class="active"{% endblock %} 7 | 8 | {% block all-content %} 9 | 10 | {{ theses_list|json_script:"theses-data" }} 11 | 12 |
13 |

14 | Prace dyplomowe 15 |

16 | {% if user.employee %} 17 | Nowa praca dyplomowa 18 | {% endif %} 19 |
20 | 21 |

22 | 23 |

24 | 25 |

26 | 27 |

28 | 29 | 30 | {% endblock %} 31 | 32 | {% block rendered_bundles %} 33 | {% render_bundle 'theses-theses-widget' %} 34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /zapisy/apps/common/assets/markdown/markdown-editor.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import MarkdownEditor from "./MarkdownEditor.vue"; 3 | 4 | document.addEventListener("DOMContentLoaded", function () { 5 | // will be a replaced with a widget. 6 | const elements = document.querySelectorAll("markdown-editor"); 7 | 8 | for (const el of elements) { 9 | const attrName = el.getAttribute("name") || ""; 10 | const attrValue = el.getAttribute("value") || ""; 11 | const attrPlaceholder = el.getAttribute("placeholder") || ""; 12 | const attrIsInvalid = el.classList.contains("is-invalid"); 13 | 14 | new Vue({ 15 | el: el, 16 | render: function (h) { 17 | return h(MarkdownEditor, { 18 | props: { 19 | name: attrName, 20 | value: attrValue, 21 | placeholder: attrPlaceholder, 22 | is_invalid: attrIsInvalid, 23 | }, 24 | }); 25 | }, 26 | }); 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /zapisy/apps/users/templates/users/form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block bread %} 5 | 6 | 7 | 8 | {% endblock %} 9 | 10 | {% block main-subtitle %}Zmień dane użytkownika{% endblock %} 11 | 12 | {% block all-content %} 13 |
{% csrf_token %} 14 |
15 |
16 | Zmień dane użytkownika 17 |
18 |
19 | {{ form|crispy }} 20 |
21 | 24 |
25 |
26 | {% endblock %} -------------------------------------------------------------------------------- /zapisy/apps/users/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.my_profile, name='my-profile'), 7 | path('email-change/', views.email_change, name='email-change'), 8 | path('employee-data-change/', views.employee_data_change, name='employee-data-change'), 9 | path('employees/', views.employees_view, name='employees-list'), 10 | path('students/', views.students_view, name='students-list'), 11 | path('employees//', views.employees_view, name='employee-profile'), 12 | path('employees//semester//', views.employees_view, name='employee-profile-semester'), 13 | path('students//', views.students_view, name='student-profile'), 14 | path('students//semester//', views.students_view, name='student-profile-semester'), 15 | path('personal-data-consent/', views.personal_data_consent, name='personal_data_consent'), 16 | ] 17 | -------------------------------------------------------------------------------- /zapisy/scripts/ectsimport_new.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ObjectDoesNotExist 2 | from zapisy.apps.users.models import Student 3 | 4 | ECTS_FILE = 'ects2016.txt' 5 | 6 | 7 | def deactivate_all(): 8 | students = Student.objects.all() 9 | for s in students: 10 | s.status = 1 11 | s.save() 12 | 13 | 14 | def process(line): 15 | line = line.strip() 16 | matricula, ects = line.split('|') 17 | ects = int(ects) 18 | try: 19 | student = Student.objects.get(matricula=matricula) 20 | except ObjectDoesNotExist: 21 | print("***" + str(matricula) + " brak " + str(ects)) 22 | return 23 | print(student, student.ects, ects) 24 | student.ects = max(student.ects, ects) 25 | student.status = 0 26 | student.save() 27 | 28 | 29 | def import_ects(file): 30 | for line in file: 31 | process(line) 32 | 33 | 34 | def run(): 35 | deactivate_all() 36 | file = open(ECTS_FILE) 37 | import_ects(file) 38 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0003_auto_20170606_1842.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('courses', '0002_auto_20170601_1122'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name='coursedescription', 13 | name='created', 14 | field=models.DateTimeField(auto_now_add=True), 15 | ), 16 | migrations.AlterField( 17 | model_name='courseentity', 18 | name='effects', 19 | field=models.ManyToManyField(to='courses.Effects', verbose_name='Grupa efekt\xf3w kszta\u0142cenia', blank=True), 20 | ), 21 | migrations.AlterField( 22 | model_name='term', 23 | name='classrooms', 24 | field=models.ManyToManyField(related_name='new_classrooms', verbose_name=b'sale', to='courses.Classroom', blank=True), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /zapisy/apps/theses/enums.py: -------------------------------------------------------------------------------- 1 | """Defines several thesis-related enums. 2 | 3 | Using a separate file for these definitions helps prevent 4 | circular dependencies 5 | """ 6 | from django.db import models 7 | 8 | 9 | class ThesisKind(models.IntegerChoices): 10 | MASTERS = 0, "mgr" 11 | ENGINEERS = 1, "inż" 12 | BACHELORS = 2, "lic" 13 | ISIM = 3, "isim" 14 | # Certain theses will be appropriate for both bachelor and engineer degrees 15 | BACHELORS_ENGINEERS = 4, "lic+inż" 16 | BACHELORS_ENGINEERS_ISIM = 5, "lic+inż+isim" 17 | 18 | 19 | class ThesisStatus(models.IntegerChoices): 20 | BEING_EVALUATED = 1, "weryfikowana przez komisję" 21 | RETURNED_FOR_CORRECTIONS = 2, "zwrócona do poprawek" 22 | ACCEPTED = 3, "zaakceptowana" 23 | IN_PROGRESS = 4, "w realizacji" 24 | DEFENDED = 5, "obroniona" 25 | 26 | 27 | class ThesisVote(models.IntegerChoices): 28 | NONE = 1, "brak głosu" 29 | REJECTED = 2, "odrzucona" 30 | ACCEPTED = 3, "zaakceptowana" 31 | -------------------------------------------------------------------------------- /zapisy/apps/theses/migrations/0003_create_theses_board_group.py: -------------------------------------------------------------------------------- 1 | """ 2 | This migration creates a thesis board group; 3 | users can then be added to this group using the admin interface. 4 | """ 5 | 6 | from django.db import migrations 7 | from ..users import THESIS_BOARD_GROUP_NAME 8 | 9 | 10 | def create_group(apps, schema_editor): 11 | Group = apps.get_model("auth", "Group") 12 | db_alias = schema_editor.connection.alias 13 | Group.objects.using(db_alias).create(name=THESIS_BOARD_GROUP_NAME) 14 | 15 | 16 | def remove_group(apps, schema_editor): 17 | Group = apps.get_model("auth", "Group") 18 | db_alias = schema_editor.connection.alias 19 | Group.objects.using(db_alias).filter(name=THESIS_BOARD_GROUP_NAME).delete() 20 | 21 | 22 | class Migration(migrations.Migration): 23 | 24 | dependencies = [ 25 | ('theses', '0002_create_system_settings'), 26 | ] 27 | 28 | operations = [ 29 | migrations.RunPython(create_group, remove_group) 30 | ] 31 | -------------------------------------------------------------------------------- /zapisy/requirements.common.txt: -------------------------------------------------------------------------------- 1 | Django==3.1.14 2 | django-bootstrap-pagination==1.7.1 3 | django-cas-ng==4.3.0 4 | django-crispy-forms==1.14.0 5 | django-environ==0.9.0 6 | django-extensions==2.2.9 7 | django-filter==2.4.0 8 | django-mailer==1.2.6 9 | redis==3.5.3 10 | rq==1.16.2 11 | django-rq==2.8.1 12 | django-test-without-migrations==0.6 13 | django-webpack-loader==0.7.0 14 | djangorestframework==3.15.1 15 | django-pagedown==2.2.1 16 | 17 | ipdb==0.13.13 18 | prompt-toolkit==3.0.50 19 | ipython==7.34.0 20 | pycryptodome==3.21.0 21 | rollbar==0.16.3 22 | newrelic==5.24.0.153 23 | gunicorn==20.1.0 24 | python-dateutil==2.9.0.post0 25 | python-memcached==1.62 26 | more-itertools==8.14.0 27 | # PyYAML is used for fixtures. 28 | pyyaml==6.0.2 29 | # Bokeh - library used for plotting in views displaying Poll results 30 | bokeh==2.4.3 31 | 32 | gspread==3.7.0 33 | typing-extensions==3.10.0.2 34 | oauth2client==4.1.3 35 | # We use html2text for one-off migration from HTML to markdown. 36 | html2text 37 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0006_auto_20171216_1754.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.9.13 on 2017-12-16 17:54 2 | from django_extensions.db.fields import AutoSlugField 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0005_auto_20171112_2306'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='classroom', 15 | name='slug', 16 | field=AutoSlugField(editable=False, populate_from='number'), 17 | ), 18 | migrations.AlterField( 19 | model_name='classroom', 20 | name='type', 21 | field=models.IntegerField(choices=[(0, 'Sala wyk\u0142adowa'), (1, 'Sala \u0107wiczeniowa'), (2, 'Pracownia komputerowa - Windows'), (3, 'Pracownia komputerowa - Linux'), (4, 'Pracownia dwusystemowa (Windows+Linux)'), (5, 'Poligon (109)')], default=1, verbose_name=b'typ'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/assets/report.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | // zaznacz wszystkie sale do raportu 3 | document.querySelectorAll(".select-all-button").forEach((e) => { 4 | const grandpa = e.parentElement.parentElement; 5 | const select = grandpa.querySelector("select[name=rooms]"); 6 | if (select === undefined) { 7 | return; 8 | } 9 | e.onclick = (event) => { 10 | for (const opt of select.options) { 11 | opt.selected = true; 12 | } 13 | }; 14 | }); 15 | 16 | // Print button. 17 | const printButton = document.getElementById("print-report"); 18 | if (printButton) { 19 | printButton.addEventListener("click", () => { 20 | window.print(); 21 | }); 22 | } 23 | 24 | const roomSelects = document.querySelectorAll("select[name=rooms]"); 25 | roomSelects.forEach((el) => { 26 | el.classList.add("form-control"); 27 | }); 28 | 29 | document.getElementById("id_week").classList.add("form-control"); 30 | }); 31 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/timetable/assets/prototype-index.js: -------------------------------------------------------------------------------- 1 | // Instantiates Timetable prototype component. 2 | // 3 | // It will put the prototype component in the DOM element with id #timetable. 4 | // Two sets of data is read — the description of all the courses, and the 5 | // description of groups the student is enqueued/enrolled into. Two elements are used for this, one with id 7 | // #courses-list, the other with id #timetable-data. For details look into 8 | // `store/{groups.ts, courses.ts}`. 9 | 10 | import Vue from "vue"; 11 | 12 | import Prototype from "./components/Prototype.vue"; 13 | import CourseList from "./components/CourseList.vue"; 14 | import CourseFilter from "./components/CourseFilter.vue"; 15 | import store from "./store"; 16 | 17 | new Vue({ el: "#timetable", render: (h) => h(Prototype), store }); 18 | new Vue({ el: "#course-filter", render: (h) => h(CourseFilter), store }); 19 | new Vue({ el: "#course-list", render: (h) => h(CourseList), store }); 20 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0020_auto_20200202_1753.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.13 on 2020-02-02 17:53 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('users', '0019_remove_email_change_model_from_email_change_app'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='employee', 17 | name='user', 18 | field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='employee_ptr', to=settings.AUTH_USER_MODEL, verbose_name='Użytkownik'), 19 | ), 20 | migrations.AlterField( 21 | model_name='student', 22 | name='user', 23 | field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='student_ptr', to=settings.AUTH_USER_MODEL, verbose_name='Użytkownik'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/templates/courses/courses.html: -------------------------------------------------------------------------------- 1 | {% extends "courses/base.html" %} 2 | 3 | {% block main-subtitle %} 4 | {{ course|default:"Przedmioty" }} 5 | {% endblock %} 6 | 7 | {% block bread %} 8 | 11 | 12 | 15 | 16 | {% if course %} 17 | 20 | {% endif %} 21 | {% endblock %} 22 | 23 | 24 | {% block enrollment_menu_courses %} class="active"{% endblock %} 25 | 26 | {% block content %} 27 | {% if course %} 28 | {% include "courses/course_parts/course_info.html" with course=course %} 29 | {% else %} 30 | 31 | {% endif %} 32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /zapisy/apps/statistics/templates/statistics/base.html: -------------------------------------------------------------------------------- 1 | {% extends "enrollment/base.html" %} 2 | 3 | {% block main-subtitle %}Statystyki{% endblock %} 4 | 5 | {% block bread %} 6 | 7 | 8 | {% endblock %} 9 | 10 | {% block enrollment_statistics %}class="active"{% endblock %} 11 | 12 | {% block all-content %} 13 | 14 | 24 |
25 | {% block statistics-content %}{% endblock %} 26 |
27 | 28 | {% endblock all-content %} -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0005_auto_20171112_2306.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('courses', '0004_auto_20171019_1034'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name='changedday', 13 | name='weekday', 14 | field=models.CharField(max_length=1, verbose_name=b'zmieniony na', choices=[(b'1', 'poniedzia\u0142ek'), (b'2', 'wtorek'), (b'3', '\u015broda'), (b'4', 'czwartek'), (b'5', 'pi\u0105tek'), (b'6', 'sobota'), (b'7', 'niedziela')]), 15 | ), 16 | migrations.AlterField( 17 | model_name='term', 18 | name='dayOfWeek', 19 | field=models.CharField(max_length=1, verbose_name=b'dzie\xc5\x84 tygodnia', choices=[(b'1', 'poniedzia\u0142ek'), (b'2', 'wtorek'), (b'3', '\u015broda'), (b'4', 'czwartek'), (b'5', 'pi\u0105tek'), (b'6', 'sobota'), (b'7', 'niedziela')]), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /infra/playbooks/newrelic.yml: -------------------------------------------------------------------------------- 1 | - name: Add New Relic APT Key 2 | apt_key: 3 | id: A758B3FBCD43BE8D123A3476BB29EE038ECCE87C 4 | url: https://download.newrelic.com/infrastructure_agent/gpg/newrelic-infra.gpg 5 | state: present 6 | 7 | - name: Add New Relic APT Repository 8 | apt_repository: 9 | repo: deb [arch=amd64] https://download.newrelic.com/infrastructure_agent/linux/apt bionic main 10 | state: present 11 | filename: newrelic 12 | 13 | - name: Add licence key to New Relic agent configuration 14 | template: 15 | src: templates/newrelic-infra.yml.j2 16 | dest: /etc/newrelic-infra.yml 17 | 18 | - name: Install the New Relic Agent 19 | apt: 20 | pkg: newrelic-infra 21 | 22 | - name: Add logs configuration to New Relic. 23 | template: 24 | src: templates/newrelic-server-logs.yml.j2 25 | dest: /etc/newrelic-infra/logging.d/server-logs.yml 26 | 27 | - name: Reload the New Relic Agent as there may be a new configuration 28 | service: 29 | name: newrelic-infra 30 | state: restarted 31 | -------------------------------------------------------------------------------- /zapisy/templates/maintenance_off.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Czas serwisowy 6 | 14 | 15 | 16 |
17 |

Będziemy z powrotem wkrótce!

18 |
19 |

Przepraszamy za niedogodności, ale przeprowadzamy w tym momencie prace serwisowe. Jeżeli potrzebujesz, zawsze możesz skontaktować się z nami, w przeciwnym razie prosimy o chwilę cierpliwości.

20 |

— Zespół zapisy.ii.uni.wroc.pl

21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /zapisy/apps/theses/users.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | 3 | from apps.theses.system_settings import get_master_rejecter 4 | from apps.users.models import Employee, is_employee, is_user_in_group 5 | 6 | THESIS_BOARD_GROUP_NAME = "Komisja prac dyplomowych" 7 | 8 | 9 | def get_theses_board(): 10 | """Returns all members of the theses board.""" 11 | return Employee.objects.select_related( 12 | 'user' 13 | ).filter(user__groups__name=THESIS_BOARD_GROUP_NAME) 14 | 15 | 16 | def get_num_board_members() -> int: 17 | """Returns the number of theses board members.""" 18 | return get_theses_board().count() 19 | 20 | 21 | def is_theses_board_member(user: User) -> bool: 22 | """Is the specified user a member of the theses board?""" 23 | return is_user_in_group(user, THESIS_BOARD_GROUP_NAME) 24 | 25 | 26 | def is_master_rejecter(user: User) -> bool: 27 | """Is the specified user a master rejecter of theses board?""" 28 | return is_employee(user) and get_master_rejecter() == user.employee 29 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/templates/poll/tickets_enter.html: -------------------------------------------------------------------------------- 1 | {% extends "grade/base.html" %} 2 | 3 | {% load crispy_forms_tags %} 4 | 5 | {% block main-subtitle %}Wprowadzanie kluczy{% endblock %} 6 | {% block nav-grade-tickets-enter %}active{% endblock %} 7 | 8 | {% block bread %} 9 | {% include 'grade/_breadcrumbs.html' %} 10 | 11 | {% endblock %} 12 | 13 | {% block all-content %} 14 | {% if user.is_authenticated %} 15 | {% include 'poll/_user_is_authenticated.html' %} 16 | {% else %} 17 |
18 |
19 | {% csrf_token %} 20 |

Wprowadzanie kluczy

21 | {{ form|crispy }} 22 |

Jeżeli nie posiadasz jeszcze kluczy, zaloguj się i je wygeneruj.

23 | 24 |
25 |
26 | {% endif %} 27 | {% endblock %} -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0021_auto_20200219_2305.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.13 on 2020-02-19 23:05 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('users', '0020_auto_20200202_1753'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='employee', 17 | name='user', 18 | field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='employee_ptr', related_query_name='employee', to=settings.AUTH_USER_MODEL, verbose_name='Użytkownik'), 19 | ), 20 | migrations.AlterField( 21 | model_name='student', 22 | name='user', 23 | field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='student_ptr', related_query_name='student', to=settings.AUTH_USER_MODEL, verbose_name='Użytkownik'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/assets/components/CounterComponent.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 33 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/templates/courses/course_parts/course_consultations.html: -------------------------------------------------------------------------------- 1 |

Konsultacje prowadzących:

2 |
3 | 4 | {% if teachers %} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {% for teacher in teachers %} 15 | {% if teacher %} 16 | 17 | 19 | 20 | 21 | 22 | {% endif %} 23 | {% endfor %} 24 | 25 |
Imię i nazwiskoPokójKonsultacje
18 | {{ teacher.user.get_full_name }}{{ teacher.room|default:"" }}{{ teacher.consultations }}
26 |
27 | {% else %} 28 | Brak informacji. 29 | {% endif %} 30 | -------------------------------------------------------------------------------- /zapisy/apps/offer/vote/migrations/0004_data_migration.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | class Migration(migrations.Migration): 4 | 5 | dependencies = [ 6 | ('vote', '0003_auto_20190414_1147'), 7 | ] 8 | 9 | operations = [ 10 | migrations.RunSQL('''DELETE FROM vote_singlevote v1 11 | USING vote_singlevote v2 12 | WHERE 13 | v1.student_id = v2.student_id 14 | AND v1.entity_id = v2.entity_id 15 | AND v1.state_id = v2.state_id 16 | AND v1.id < v2.id 17 | ''', # Deletes duplicate votes. 18 | reverse_sql=migrations.RunSQL.noop, 19 | ), 20 | migrations.RunSQL([ 21 | '''UPDATE vote_singlevote 22 | SET proposal_id = i.id 23 | FROM 24 | courses_courseinformation i 25 | WHERE vote_singlevote.entity_id = i.entity_id 26 | '''], reverse_sql=migrations.RunSQL.noop 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/fixtures/courses.yaml: -------------------------------------------------------------------------------- 1 | - model: courses.semester 2 | pk: 1 3 | fields: 4 | visible: true 5 | type: z # Semester.TYPE_WINTER 6 | year: 2018/19 7 | records_opening: 2018-10-01 00:00:00 8 | records_closing: 2090-12-21 23:59:59 9 | semester_beginning: 2018-10-3 10 | semester_ending: 2090-12-31 11 | 12 | - model: courses.type 13 | pk: 1 14 | fields: {} 15 | 16 | - model: courses.courseinformation 17 | pk: 1 18 | fields: 19 | name: Algorytmy i Struktury Danych 20 | course_type_id: 1 21 | owner_id: 2 22 | slug: algo 23 | created: 1970-01-01 00:00:00 24 | modified: 1970-01-01 00:00:00 25 | 26 | - model: courses.courseinstance 27 | fields: 28 | courseinformation_ptr: 1 29 | semester: 1 30 | 31 | - model: courses.group 32 | pk: 1 33 | fields: 34 | limit: 20 35 | type: 2 # 'cwiczenia' 36 | teacher: 2 37 | course: 1 38 | 39 | - model: courses.group 40 | pk: 2 41 | fields: 42 | limit: 20 43 | type: 2 # 'cwiczenia' 44 | teacher: 3 45 | course: 1 46 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/admin.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.contrib import admin 4 | from django.forms import widgets, JSONField 5 | 6 | from .models import Schema 7 | 8 | 9 | class PrettyJSONWidget(widgets.Textarea): 10 | """Formats JSON in a textarea with an indentation.""" 11 | def format_value(self, value): 12 | try: 13 | value = json.dumps(json.loads(value), indent=2, ensure_ascii=False) 14 | # these lines will try to adjust size of TextArea to fit to content 15 | row_lengths = [len(r) for r in value.split('\n')] 16 | self.attrs['rows'] = min(max(len(row_lengths) + 2, 10), 30) 17 | self.attrs['cols'] = min(max(max(row_lengths) + 2, 40), 120) 18 | return value 19 | except Exception: 20 | return super(PrettyJSONWidget, self).format_value(value) 21 | 22 | 23 | @admin.register(Schema) 24 | class SchemaAdmin(admin.ModelAdmin): 25 | list_display = ('type',) 26 | 27 | formfield_overrides = { 28 | JSONField: {'widget': PrettyJSONWidget} 29 | } 30 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/templates/proposal/proposal.html: -------------------------------------------------------------------------------- 1 | {% load proposal_status %} 2 | 3 |
4 |

{{course.name}}

5 |
6 | {% if user.is_staff or course.owner == user.employee %} 7 | Edytuj 8 | {% if course.status|status_label == "draft" %} 9 | 12 | Usuń szkic 13 | 14 | {% endif %} 15 | {% endif %} 16 | {% if user.employee %} 17 | Sklonuj 18 | {% endif %} 19 |
20 |
21 | 22 | {% include 'proposal/proposal_info.html' with course=course %} 23 | -------------------------------------------------------------------------------- /zapisy/scripts/copy_reservations.py: -------------------------------------------------------------------------------- 1 | from apps.enrollment.courses.models.semester import Semester 2 | from apps.schedule.models.specialreservation import SpecialReservation 3 | from django.contrib.auth.models import User 4 | 5 | current_year = '2018/19' 6 | next_year = '2019/20' 7 | 8 | 9 | def run(): 10 | author_id = User.objects.get(username='asm').id 11 | reservations = SpecialReservation.objects.filter( 12 | semester__year=current_year) 13 | for reservation in reservations: 14 | print(reservation) 15 | if input('Confirm (Y/n):') != 'n': 16 | new_reservation = SpecialReservation( 17 | semester=Semester.objects.get( 18 | year=next_year, 19 | type=reservation.semester.type), 20 | title=reservation.title, 21 | classroom=reservation.classroom, 22 | dayOfWeek=reservation.dayOfWeek, 23 | start_time=reservation.start_time, 24 | end_time=reservation.end_time) 25 | new_reservation.save(author_id) 26 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/courses/migrations/0015_auto_20190312_1330.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.8 on 2019-03-12 13:30 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('courses', '0014_auto_20190212_1313'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='classroom', 15 | name='usos_id', 16 | field=models.PositiveIntegerField(blank=True, null=True, unique=True, verbose_name='ID sali w systemie USOS'), 17 | ), 18 | migrations.AddField( 19 | model_name='semester', 20 | name='usos_kod', 21 | field=models.CharField(blank=True, max_length=20, null=True, unique=True, verbose_name='Kod semestru w systemie USOS'), 22 | ), 23 | migrations.AddField( 24 | model_name='term', 25 | name='usos_id', 26 | field=models.PositiveIntegerField(blank=True, null=True, unique=True, verbose_name='Kod terminu w systemie USOS'), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0003_auto_20170601_1122.py: -------------------------------------------------------------------------------- 1 | from django.db import models, migrations 2 | import datetime 3 | from django.conf import settings 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0002_auto_20170529_1617'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='employee', 15 | name='last_news_view', 16 | field=models.DateTimeField(default=datetime.datetime.now), 17 | preserve_default=True, 18 | ), 19 | migrations.AlterField( 20 | model_name='student', 21 | name='last_news_view', 22 | field=models.DateTimeField(default=datetime.datetime.now), 23 | preserve_default=True, 24 | ), 25 | migrations.AlterField( 26 | model_name='userprofile', 27 | name='user', 28 | field=models.OneToOneField(related_name='profile', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), 29 | preserve_default=True, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/migrations/0008_auto_20191119_0017.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.14 on 2019-11-19 00:17 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('schedule', '0007_auto_20190528_0101'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='specialreservation', 16 | name='classroom', 17 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Classroom', verbose_name='sala'), 18 | ), 19 | migrations.AlterField( 20 | model_name='specialreservation', 21 | name='semester', 22 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Semester', verbose_name='semestr'), 23 | ), 24 | migrations.AlterField( 25 | model_name='specialreservation', 26 | name='title', 27 | field=models.CharField(max_length=255, verbose_name='nazwa'), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0007_consents.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.9 on 2018-05-25 06:06 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('users', '0006_auto_20180525_0559'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Consents', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('granted', models.NullBooleanField(verbose_name='zgoda udzielona')), 21 | ('student', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='consent', to='users.Student')), 22 | ], 23 | options={ 24 | 'verbose_name': 'Zgoda na udostępnianie danych osobowych', 25 | 'verbose_name_plural': 'Zgody na udostępnianie danych osobowych', 26 | }, 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /infra/db_backups/slack_notifications.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from slack import WebClient 3 | 4 | 5 | def get_connected_slack_client(secrets_env): 6 | slack_client = WebClient(token=secrets_env.str('SLACK_TOKEN')) 7 | return slack_client 8 | 9 | # for real time connection 10 | # if slack_client.rtm_connect(with_team_state=False): 11 | # return slack_client 12 | #raise RuntimeError('SlackClient.rtm_connect failed') 13 | 14 | 15 | def _send_slack_msg(slack_client, channel_id: str, msg: str): 16 | slack_client.chat_postMessage( 17 | channel=channel_id, 18 | text=msg 19 | ) 20 | 21 | 22 | def send_success_notification(slack_client, dev_db_link: str, seconds_elapsed: int, channel_id: str): 23 | msg = f'Databases backed up successfully in {seconds_elapsed} seconds. *Dev DB download link:* {dev_db_link}' 24 | _send_slack_msg(slack_client, channel_id, msg) 25 | 26 | 27 | def send_error_notification(slack_client, error_msg: str, channel_id: str): 28 | msg = f'*Failed to back up databases:*\n```{error_msg}```' 29 | _send_slack_msg(slack_client, channel_id, msg) 30 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/custom_signals.py: -------------------------------------------------------------------------------- 1 | import django.dispatch 2 | 3 | # Signal senders must provide `instance` and `user` arguments. 4 | # instance: apps.enrollment.courses.models.Group 5 | # user: django.contrib.auth.models.User 6 | student_pulled = django.dispatch.Signal() 7 | # Signal senders must provide the following arguments: 8 | # instance: apps.enrollment.courses.models.Group 9 | # user: django.contrib.auth.models.User 10 | # reason: str 11 | student_not_pulled = django.dispatch.Signal() 12 | # Signal senders must provide the following arguments: 13 | # instance: apps.enrollment.courses.models.Group 14 | # teacher: apps.users.models.Employee 15 | teacher_changed = django.dispatch.Signal() 16 | # Signal senders must provide an argument: 17 | # instance: apps.theses.models.Thesis 18 | thesis_voting_activated = django.dispatch.Signal() 19 | # Signal senders must provide an argument: 20 | # event: apps.schedule.models.event 21 | event_decision = django.dispatch.Signal() 22 | # Signal senders must provide an argument: 23 | # instance: apps.theses.models.Thesis 24 | thesis_accepted = django.dispatch.Signal() 25 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/migrations/0006_proposal_semester.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-05-12 22:12 2 | 3 | import apps.offer.proposal.models 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('proposal', '0005_proposal'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='proposal', 16 | name='smtr', 17 | field=models.CharField(choices=[(apps.offer.proposal.models.SemesterChoices('u'), 'nieokreślony'), (apps.offer.proposal.models.SemesterChoices('z'), 'zimowy'), (apps.offer.proposal.models.SemesterChoices('l'), 'letni')], default=apps.offer.proposal.models.SemesterChoices('u'), max_length=1, verbose_name='semestr'), 18 | ), 19 | migrations.RunSQL( 20 | '''UPDATE proposal_proposal 21 | SET smtr=courses_courseinformation.semester 22 | FROM courses_courseinformation 23 | WHERE proposal_proposal.courseinformation_ptr_id=courses_courseinformation.id; 24 | ''', 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /zapisy/apps/schedulersync/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.10.8 on 2018-01-24 16:03 2 | 3 | 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ('courses', '0006_auto_20171216_1754'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='TermSyncData', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('scheduler_id', models.PositiveIntegerField(null=True, unique=True, verbose_name='id grupy w schedulerze')), 22 | ('term', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Term', verbose_name='termin')), 23 | ], 24 | options={ 25 | 'verbose_name': 'Obiekt synchronizacji termin\xf3w grup', 26 | 'verbose_name_plural': 'Obiekty synchronizacji termin\xf3w grup', 27 | }, 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /zapisy/apps/effects/models.py: -------------------------------------------------------------------------------- 1 | from typing import Set 2 | 3 | from django.db import models 4 | 5 | from apps.enrollment.courses.models.course_instance import CourseInstance 6 | from apps.users.models import Program, Student 7 | 8 | 9 | # Model for completed courses 10 | class CompletedCourses(models.Model): 11 | student = models.ForeignKey(Student, on_delete=models.CASCADE) 12 | course = models.ForeignKey(CourseInstance, on_delete=models.CASCADE) 13 | program = models.ForeignKey(Program, on_delete=models.CASCADE) 14 | 15 | class Meta: 16 | unique_together = ('student', 'course', 'program') 17 | 18 | def get_completed_effects(student: Student) -> Set[str]: 19 | completed_courses = ( 20 | CompletedCourses.objects.filter(student=student, program=student.program) 21 | .select_related('course').prefetch_related('course__effects') 22 | ) 23 | 24 | done_effects = set() 25 | for record in completed_courses: 26 | for effect in record.course.effects.all(): 27 | done_effects.add(effect.group_name) 28 | 29 | return done_effects 30 | -------------------------------------------------------------------------------- /zapisy/apps/offer/preferences/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.forms.models import modelformset_factory 3 | 4 | from apps.users.models import Employee 5 | 6 | from .models import Preference 7 | 8 | 9 | class PreferenceForm(forms.ModelForm): 10 | class Meta: 11 | model = Preference 12 | fields = ('answer',) 13 | labels = { 14 | 'answer': "", 15 | } 16 | 17 | def __init__(self, *args, **kwargs): 18 | super(PreferenceForm, self).__init__(*args, **kwargs) 19 | self.fields['answer'].widget.attrs['class'] = 'form-select' 20 | 21 | 22 | def prepare_formset(employee: Employee, post=None): 23 | """Creates missing vote objects and returns a formset for the employee.""" 24 | Preference.make_preferences(employee) 25 | PreferenceFormset = modelformset_factory(Preference, form=PreferenceForm, extra=0) 26 | qs = Preference.objects.filter(employee=employee).order_by('question__proposal') 27 | if post: 28 | formset = PreferenceFormset(post, queryset=qs) 29 | else: 30 | formset = PreferenceFormset(queryset=qs) 31 | return formset 32 | -------------------------------------------------------------------------------- /zapisy/apps/common/assets/markdown/render-markdown.ts: -------------------------------------------------------------------------------- 1 | import MarkdownIt from "markdown-it"; 2 | 3 | const md = MarkdownIt({ 4 | linkify: true, 5 | typographer: true, 6 | quotes: "„”«»", 7 | }); 8 | 9 | let callback = function (_mutations: any, _observer: any) { 10 | for (const element of document.querySelectorAll(".markdown")) { 11 | const raw = element.textContent || ""; 12 | const rendered = md.render(raw); 13 | element.innerHTML = rendered; 14 | // This way Markdown is not rendered twice. 15 | element.classList.remove("markdown"); 16 | element.classList.add("markdown-rendered"); 17 | } 18 | }; 19 | 20 | // Options for the observer (which mutations to observe) 21 | const config = { attributes: false, childList: true, subtree: false }; 22 | // Bind to either #main-content (a view with sidebar like the courses page) or 23 | // #main-content-container (a full-page view like my-proposals). 24 | const targetNode = 25 | document.getElementById("main-content") || 26 | document.getElementById("main-content-container")!; 27 | let observer = new MutationObserver(callback); 28 | observer.observe(targetNode, config); 29 | -------------------------------------------------------------------------------- /zapisy/templates/semester_dropdown.html: -------------------------------------------------------------------------------- 1 | 25 | -------------------------------------------------------------------------------- /zapisy/apps/notifications/migrations/0005_auto_20190929_1436.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.8 on 2019-09-29 14:36 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('notifications', '0004_auto_20190628_0219'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='notificationpreferencesstudent', 15 | name='added_new_group', 16 | field=models.BooleanField(default=False, verbose_name='Dodanie nowej grupy przedmiotu, na który jesteś zapisany/a'), 17 | ), 18 | migrations.AlterField( 19 | model_name='notificationpreferencesstudent', 20 | name='not_pulled_from_queue', 21 | field=models.BooleanField(default=False, verbose_name='Niepowodzenie wciągnięcia Cię do grupy'), 22 | ), 23 | migrations.AlterField( 24 | model_name='notificationpreferencesstudent', 25 | name='pulled_from_queue', 26 | field=models.BooleanField(default=False, verbose_name='Zapisanie Cię do grupy'), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0009_auto_20180420_2334.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11.9 on 2018-04-20 23:34 2 | from django.db import migrations 3 | 4 | 5 | def add_respective_users_to_groups(apps, schema_editor): 6 | Group = apps.get_model("auth", "Group") 7 | Employee = apps.get_model("users", "Employee") 8 | Student = apps.get_model("users", "Student") 9 | db_alias = schema_editor.connection.alias 10 | 11 | students, created = Group.objects.using(db_alias).get_or_create(name='students') 12 | employees, created = Group.objects.using(db_alias).get_or_create(name='employees') 13 | 14 | for employee in Employee.objects.using(db_alias).all(): 15 | user = employee.user 16 | employees.user_set.add(user) 17 | 18 | for student in Student.objects.using(db_alias).all(): 19 | user = student.user 20 | students.user_set.add(user) 21 | 22 | 23 | class Migration(migrations.Migration): 24 | dependencies = [ 25 | ('users', '0008_auto_20180601_1918'), 26 | ] 27 | 28 | operations = [ 29 | migrations.RunPython(add_respective_users_to_groups, reverse_code=migrations.RunPython.noop), 30 | ] 31 | -------------------------------------------------------------------------------- /zapisy/apps/schedule/templates/schedule/includes/_report_form_render.html: -------------------------------------------------------------------------------- 1 | {% csrf_token %} 2 | {{ form.non_field_errors }} 3 | {% for hidden in form.hidden_fields %} 4 | {{ hidden }} 5 | {% endfor %} 6 | 7 |
8 |
9 | {{ form_table.rooms }} 10 | 15 |
16 | {{ form.rooms.errors }} 17 |
18 | 19 | {% if form.beg_date and form.end_date %} 20 |
21 |
22 | 23 | {{ form_table.beg_date }} 24 | 25 | {{ form_table.end_date }} 26 |
27 |
28 | {% endif %} 29 | 30 | {% if form.week %} 31 |
32 | {{ form.week }} 33 | {{ form.week.errors }} 34 |
35 | {% endif %} -------------------------------------------------------------------------------- /zapisy/apps/effects/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.5 on 2021-02-05 10:44 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('courses', '0034_auto_20201106_2039'), 13 | ('users', '0024_auto_20201029_1347'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='CompletedCourses', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.courseinstance')), 22 | ('program', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.program')), 23 | ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.student')), 24 | ], 25 | options={ 26 | 'unique_together': {('student', 'course', 'program')}, 27 | }, 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /zapisy/apps/users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.contrib.auth import get_user_model 3 | 4 | 5 | class UsersConfig(AppConfig): 6 | name = 'apps.users' 7 | verbose_name = 'Users' 8 | 9 | def ready(self): 10 | """Monkey-patches auth.User models to provide Student and Employee.""" 11 | super(UsersConfig, self).ready() 12 | 13 | def get_student(self): 14 | if hasattr(self, 'student_ptr'): 15 | return self.student_ptr 16 | else: 17 | return None 18 | 19 | def get_employee(self): 20 | if hasattr(self, 'employee_ptr'): 21 | return self.employee_ptr 22 | else: 23 | return None 24 | 25 | UserModel = get_user_model() 26 | setattr(UserModel, 'student', property(get_student)) 27 | setattr(UserModel, 'employee', property(get_employee)) 28 | 29 | from django.contrib.auth import models as auth_models 30 | setattr(auth_models.AnonymousUser, 'student', None) 31 | setattr(auth_models.AnonymousUser, 'employee', None) 32 | 33 | import apps.users.signals # noqa 34 | -------------------------------------------------------------------------------- /zapisy/apps/users/migrations/0017_auto_20191020_1023.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.13 on 2019-10-20 10:23 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0016_remove_program_type_of_points'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='employee', 15 | name='receive_mass_mail_enrollment', 16 | ), 17 | migrations.RemoveField( 18 | model_name='employee', 19 | name='receive_mass_mail_grade', 20 | ), 21 | migrations.RemoveField( 22 | model_name='employee', 23 | name='receive_mass_mail_offer', 24 | ), 25 | migrations.RemoveField( 26 | model_name='student', 27 | name='receive_mass_mail_enrollment', 28 | ), 29 | migrations.RemoveField( 30 | model_name='student', 31 | name='receive_mass_mail_grade', 32 | ), 33 | migrations.RemoveField( 34 | model_name='student', 35 | name='receive_mass_mail_offer', 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /zapisy/apps/offer/proposal/migrations/0002_auto_20170529_1617.py: -------------------------------------------------------------------------------- 1 | from django.db import models, migrations 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('proposal', '0001_initial'), 8 | ('courses', '0001_initial'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='syllabus', 14 | name='entity', 15 | field=models.OneToOneField(related_name='syllabus', verbose_name=b'podstawa przedmiotu', to='courses.CourseEntity', on_delete=models.CASCADE), 16 | preserve_default=True, 17 | ), 18 | migrations.AddField( 19 | model_name='syllabus', 20 | name='learning_methods', 21 | field=models.ManyToManyField(to='proposal.LearningMethod', null=True, verbose_name='Metody kszta\u0142cenia', blank=True), 22 | preserve_default=True, 23 | ), 24 | migrations.AddField( 25 | model_name='studentwork', 26 | name='syllabus', 27 | field=models.ForeignKey(to='proposal.Syllabus', on_delete=models.CASCADE), 28 | preserve_default=True, 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /zapisy/apps/grade/poll/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.TicketsEntry.as_view(), name='grade-poll-main'), 7 | path('enter/', views.TicketsEntry.as_view(), name='grade-poll-tickets-enter'), 8 | path('submissions/', 9 | views.SubmissionEntry.as_view(), {'submission_index': 0}, 10 | name='grade-poll-submissions'), 11 | path('submissions//', 12 | views.SubmissionEntry.as_view(), 13 | name='grade-poll-submissions'), 14 | path('results/', views.PollResults.as_view(), name='grade-poll-results'), 15 | path('results/semester//', 16 | views.PollResults.as_view(), 17 | name='grade-poll-results'), 18 | path('results/semester//poll//', 19 | views.PollResults.as_view(), 20 | name='grade-poll-results'), 21 | path('results/semester//poll//submission//', 22 | views.PollResults.as_view(), 23 | name='grade-poll-results'), 24 | path('clear/', views.ClearSession.as_view(), name='grade-poll-clear-session'), 25 | ] 26 | -------------------------------------------------------------------------------- /zapisy/scripts/clear_queues.py: -------------------------------------------------------------------------------- 1 | from apps.enrollment.courses.models import Group 2 | from apps.enrollment.records.models import Record, RecordStatus 3 | from apps.enrollment.courses.models.group import GuaranteedSpots 4 | from django.contrib.auth.models import Group as AuthGroup 5 | 6 | 7 | def run(): 8 | turns = ['tura1', 'tura2', 'tura3'] 9 | course_slugs = [ 10 | 'analiza-matematyczna-201920-zimowy', 'kurs-podstawowy-warsztat-informatyka-201920-zimowy', 11 | 'kurs-wstep-do-programowania-w-jezyku-c-201920-zimowy', 12 | 'kurs-wstep-do-programowania-w-jezyku-python-201920-zimowy', 13 | 'wstep-do-informatyki-201920-zimowy' 14 | ] 15 | course_groups = Group.objects.filter(course__slug__in=course_slugs) 16 | roles = AuthGroup.objects.filter(name__in=turns) 17 | Record.objects.filter(group__in=course_groups, status=RecordStatus.QUEUED).delete() 18 | for gr in course_groups: 19 | limit = gr.limit 20 | for gs in GuaranteedSpots.objects.filter(group=gr, role__in=roles): 21 | limit = limit + gs.limit 22 | GuaranteedSpots.objects.filter(group=gr, role__in=roles).delete() 23 | gr.limit = limit 24 | gr.save() 25 | -------------------------------------------------------------------------------- /zapisy/apps/enrollment/records/migrations/0006_auto_20180813_2243.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0 on 2018-08-13 22:43 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('records', '0005_auto_20180805_0135'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='record', 16 | name='created', 17 | field=models.DateTimeField(auto_now_add=True), 18 | ), 19 | migrations.AlterField( 20 | model_name='record', 21 | name='modified', 22 | field=models.DateTimeField(auto_now=True), 23 | ), 24 | migrations.AlterField( 25 | model_name='record', 26 | name='status', 27 | field=models.CharField(choices=[('0', 'Queued'), ('1', 'Enrolled'), ('2', 'Removed')], max_length=1), 28 | ), 29 | migrations.AlterField( 30 | model_name='record', 31 | name='student', 32 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.Student'), 33 | ), 34 | ] 35 | --------------------------------------------------------------------------------