From 1693501d1cd615781669f71268f4b1dcb7e7dd19 Mon Sep 17 00:00:00 2001 From: CPABONG Date: Sun, 3 Aug 2025 10:28:43 +0900 Subject: [PATCH] =?UTF-8?q?Initial=20commit:=20=EC=8B=A0=EB=9D=BC=EB=8C=80?= =?UTF-8?q?=ED=95=99=EA=B5=90=20AMP=20=EC=9C=A0=EC=A0=80=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EB=B2=84=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursorrules | 16 + A_core/__init__.py | 0 A_core/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 148 bytes A_core/__pycache__/settings.cpython-38.pyc | Bin 0 -> 3104 bytes A_core/__pycache__/urls.cpython-38.pyc | Bin 0 -> 1249 bytes A_core/__pycache__/wsgi.cpython-38.pyc | Bin 0 -> 549 bytes A_core/asgi.py | 16 + A_core/settings.py | 172 + A_core/urls.py | 29 + A_core/wsgi.py | 16 + B_main/__init__.py | 0 B_main/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 148 bytes B_main/__pycache__/admin.cpython-38.pyc | Bin 0 -> 3733 bytes B_main/__pycache__/apps.cpython-38.pyc | Bin 0 -> 424 bytes B_main/__pycache__/forms.cpython-38.pyc | Bin 0 -> 7699 bytes B_main/__pycache__/models.cpython-38.pyc | Bin 0 -> 2289 bytes B_main/__pycache__/peopleinfo.cpython-38.pyc | Bin 0 -> 30496 bytes B_main/__pycache__/phonelist.cpython-38.pyc | Bin 0 -> 1033 bytes B_main/__pycache__/urls.cpython-38.pyc | Bin 0 -> 839 bytes B_main/__pycache__/views.cpython-38.pyc | Bin 0 -> 8552 bytes B_main/admin.py | 84 + B_main/apps.py | 6 + B_main/clean_duplicates.py | 102 + B_main/forms.py | 297 + B_main/manual_populate.py | 174 + B_main/migrations/0001_initial.py | 29 + B_main/migrations/0002_person_user.py | 21 + ...003_person_보일지여부_alter_person_사진.py | 23 + ...4_person_회원가입상태_alter_person_사진.py | 23 + ...eyword1_person_keyword2_person_keyword3.py | 28 + .../0006_person_모든사람보기권한.py | 18 + .../0007_remove_person_보일지여부.py | 17 + .../0008_remove_person_회원가입상태.py | 17 + ...eyword2_remove_person_keyword3_and_more.py | 26 + ..._person_options_person_비밀번호설정필요.py | 22 + B_main/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-38.pyc | Bin 0 -> 959 bytes .../0002_person_user.cpython-38.pyc | Bin 0 -> 780 bytes ...일지여부_alter_person_사진.cpython-38.pyc | Bin 0 -> 818 bytes ...원가입상태_alter_person_사진.cpython-38.pyc | Bin 0 -> 950 bytes ...on_keyword2_person_keyword3.cpython-38.pyc | Bin 0 -> 1031 bytes ...006_person_모든사람보기권한.cpython-38.pyc | Bin 0 -> 834 bytes ...07_remove_person_보일지여부.cpython-38.pyc | Bin 0 -> 569 bytes ..._remove_person_회원가입상태.cpython-38.pyc | Bin 0 -> 573 bytes ...ve_person_keyword3_and_more.cpython-38.pyc | Bin 0 -> 909 bytes ...ons_person_비밀번호설정필요.cpython-38.pyc | Bin 0 -> 977 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 159 bytes B_main/models.py | 32 + B_main/peopleinfo.py | 162 + B_main/restore_phone_dashes.py | 61 + B_main/show_all_users.py | 49 + B_main/templates/B_main/custom_signup.html | 88 + B_main/templates/B_main/main(darkmode).htm | 39 + B_main/templates/B_main/main.htm | 211 + B_main/templates/B_main/partials/card.htm | 123 + .../templates/B_main/partials/card_list.htm | 4 + B_main/templates/B_main/password.htm | 62 + .../templates/B_main/phone_verification.html | 114 + B_main/templates/B_main/profile_form.htm | 217 + B_main/templates/B_main/signup.html | 135 + B_main/tests.py | 3 + B_main/update_phone_numbers.py | 79 + B_main/urls.py | 19 + B_main/views.py | 373 + C_accounts/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 152 bytes C_accounts/__pycache__/admin.cpython-38.pyc | Bin 0 -> 193 bytes C_accounts/__pycache__/apps.cpython-38.pyc | Bin 0 -> 592 bytes C_accounts/__pycache__/forms.cpython-38.pyc | Bin 0 -> 7303 bytes .../__pycache__/middleware.cpython-38.pyc | Bin 0 -> 1117 bytes C_accounts/__pycache__/models.cpython-38.pyc | Bin 0 -> 190 bytes C_accounts/__pycache__/signals.cpython-38.pyc | Bin 0 -> 970 bytes C_accounts/__pycache__/urls.cpython-38.pyc | Bin 0 -> 590 bytes C_accounts/__pycache__/views.cpython-38.pyc | Bin 0 -> 5809 bytes C_accounts/admin.py | 3 + C_accounts/apps.py | 10 + C_accounts/forms.py | 269 + C_accounts/middleware.py | 25 + C_accounts/migrations/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 163 bytes C_accounts/models.py | 3 + C_accounts/signals.py | 24 + .../C_accounts/force_password_set.html | 182 + .../templates/C_accounts/password_change.html | 142 + .../C_accounts/password_change_logged_in.html | 197 + .../templates/C_accounts/password_reset.html | 122 + .../templates/C_accounts/profile_edit.html | 350 + C_accounts/templates/base.htm | 46 + C_accounts/templatetags/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 165 bytes .../__pycache__/form_filters.cpython-38.pyc | Bin 0 -> 416 bytes C_accounts/templatetags/form_filters.py | 7 + C_accounts/tests.py | 3 + C_accounts/urls.py | 13 + C_accounts/views.py | 271 + db.sqlite3 | Bin 0 -> 331776 bytes manage.py | 22 + media/profile_photos/Bongsu.jpg | Bin 0 -> 188323 bytes media/profile_photos/bong.jpg | Bin 0 -> 160571 bytes media/profile_photos/default_user.png | Bin 0 -> 19456 bytes media/profile_photos/image.png | Bin 0 -> 57017 bytes media/profile_photos/강경옥.png | Bin 0 -> 82487 bytes media/profile_photos/강규호.png | Bin 0 -> 44566 bytes media/profile_photos/강동혁.png | Bin 0 -> 17620 bytes media/profile_photos/강승구.png | Bin 0 -> 75374 bytes media/profile_photos/강지훈.png | Bin 0 -> 64386 bytes media/profile_photos/고현숙.png | Bin 0 -> 88024 bytes media/profile_photos/공한수.png | Bin 0 -> 46821 bytes media/profile_photos/곽기융.png | Bin 0 -> 68192 bytes media/profile_photos/권중천.png | Bin 0 -> 60240 bytes media/profile_photos/김가현.png | Bin 0 -> 76478 bytes media/profile_photos/김기재.png | Bin 0 -> 42096 bytes media/profile_photos/김기호.png | Bin 0 -> 65216 bytes media/profile_photos/김대성.png | Bin 0 -> 89838 bytes media/profile_photos/김동화.png | Bin 0 -> 87509 bytes media/profile_photos/김미경.png | Bin 0 -> 57994 bytes media/profile_photos/김미애.png | Bin 0 -> 73302 bytes media/profile_photos/김민주.png | Bin 0 -> 87024 bytes media/profile_photos/김보성.png | Bin 0 -> 57226 bytes media/profile_photos/김봉수.png | Bin 0 -> 63252 bytes media/profile_photos/김상우.png | Bin 0 -> 50293 bytes media/profile_photos/김상준.png | Bin 0 -> 70577 bytes media/profile_photos/김선이.png | Bin 0 -> 61641 bytes media/profile_photos/김성주.png | Bin 0 -> 83344 bytes media/profile_photos/김성훈.png | Bin 0 -> 72900 bytes media/profile_photos/김영하.png | Bin 0 -> 67218 bytes media/profile_photos/김영훈.png | Bin 0 -> 61091 bytes media/profile_photos/김외숙.png | Bin 0 -> 28588 bytes media/profile_photos/김용권.png | Bin 0 -> 65193 bytes media/profile_photos/김윤규.png | Bin 0 -> 53229 bytes media/profile_photos/김윤아.png | Bin 0 -> 47773 bytes media/profile_photos/김은희.png | Bin 0 -> 44245 bytes media/profile_photos/김일곤.png | Bin 0 -> 71899 bytes media/profile_photos/김재준.png | Bin 0 -> 55283 bytes media/profile_photos/김재홍.png | Bin 0 -> 52688 bytes media/profile_photos/김정호.png | Bin 0 -> 70232 bytes media/profile_photos/김준수.png | Bin 0 -> 38015 bytes media/profile_photos/김중선.png | Bin 0 -> 63802 bytes media/profile_photos/김진홍.png | Bin 0 -> 56032 bytes media/profile_photos/김태영.png | Bin 0 -> 92732 bytes media/profile_photos/김태형.png | Bin 0 -> 63794 bytes media/profile_photos/김한집.png | Bin 0 -> 97295 bytes media/profile_photos/김현우.png | Bin 0 -> 60031 bytes media/profile_photos/김현준.png | Bin 0 -> 63454 bytes media/profile_photos/김희경(수정).png | Bin 0 -> 64964 bytes media/profile_photos/김희경.png | Bin 0 -> 67532 bytes media/profile_photos/노현주.png | Bin 0 -> 89242 bytes media/profile_photos/노희숙.png | Bin 0 -> 60342 bytes media/profile_photos/마점래(수정).png | Bin 0 -> 61190 bytes media/profile_photos/마점래.png | Bin 0 -> 94033 bytes media/profile_photos/문성배.png | Bin 0 -> 50857 bytes media/profile_photos/문정순.png | Bin 0 -> 51858 bytes media/profile_photos/민수연.png | Bin 0 -> 50025 bytes media/profile_photos/민홍기.png | Bin 0 -> 46644 bytes media/profile_photos/박강범.png | Bin 0 -> 55806 bytes media/profile_photos/박경민.png | Bin 0 -> 75507 bytes media/profile_photos/박국제.png | Bin 0 -> 51045 bytes media/profile_photos/박대진.png | Bin 0 -> 77067 bytes media/profile_photos/박명숙.png | Bin 0 -> 49784 bytes media/profile_photos/박명옥.png | Bin 0 -> 73539 bytes media/profile_photos/박명진.png | Bin 0 -> 57809 bytes media/profile_photos/박민희.png | Bin 0 -> 47285 bytes media/profile_photos/박부술.png | Bin 0 -> 61043 bytes media/profile_photos/박성호.png | Bin 0 -> 56481 bytes media/profile_photos/박성훈.png | Bin 0 -> 64786 bytes media/profile_photos/박순자.png | Bin 0 -> 97803 bytes media/profile_photos/박영우.png | Bin 0 -> 78050 bytes media/profile_photos/박영해.png | Bin 0 -> 64496 bytes media/profile_photos/박재성.png | Bin 0 -> 43141 bytes media/profile_photos/박정숙.png | Bin 0 -> 71211 bytes media/profile_photos/박정은.png | Bin 0 -> 68344 bytes media/profile_photos/박종인.png | Bin 0 -> 51870 bytes media/profile_photos/박주원(수정).png | Bin 0 -> 108000 bytes media/profile_photos/박주원.png | Bin 0 -> 78769 bytes media/profile_photos/박지환.png | Bin 0 -> 58764 bytes media/profile_photos/배기중.png | Bin 0 -> 39389 bytes media/profile_photos/배범한.png | Bin 0 -> 87226 bytes media/profile_photos/배성효.png | Bin 0 -> 61657 bytes media/profile_photos/변연옥.png | Bin 0 -> 59806 bytes media/profile_photos/빈윤진.png | Bin 0 -> 70731 bytes media/profile_photos/서강섭.png | Bin 0 -> 75857 bytes media/profile_photos/서지윤.png | Bin 0 -> 54719 bytes media/profile_photos/성동화.png | Bin 0 -> 51623 bytes media/profile_photos/성충식.png | Bin 0 -> 85088 bytes media/profile_photos/손동현.png | Bin 0 -> 66936 bytes media/profile_photos/송연익.png | Bin 0 -> 83010 bytes media/profile_photos/심수현.png | Bin 0 -> 77331 bytes media/profile_photos/안복지.png | Bin 0 -> 46615 bytes media/profile_photos/안상배.png | Bin 0 -> 45958 bytes media/profile_photos/안영봉.png | Bin 0 -> 55932 bytes media/profile_photos/양재진.png | Bin 0 -> 52540 bytes media/profile_photos/어익수.png | Bin 0 -> 51299 bytes media/profile_photos/엄신아.png | Bin 0 -> 67709 bytes media/profile_photos/여은주.png | Bin 0 -> 85936 bytes media/profile_photos/예영숙.png | Bin 0 -> 45917 bytes media/profile_photos/오용택.png | Bin 0 -> 69616 bytes media/profile_photos/오원재.png | Bin 0 -> 74358 bytes media/profile_photos/유석찬.png | Bin 0 -> 70862 bytes media/profile_photos/이기영.png | Bin 0 -> 45549 bytes media/profile_photos/이대명.png | Bin 0 -> 88681 bytes media/profile_photos/이두홍.png | Bin 0 -> 76735 bytes media/profile_photos/이범민.png | Bin 0 -> 71796 bytes media/profile_photos/이상경.png | Bin 0 -> 62529 bytes media/profile_photos/이상규.png | Bin 0 -> 58812 bytes media/profile_photos/이상민.png | Bin 0 -> 51561 bytes media/profile_photos/이수연.png | Bin 0 -> 73719 bytes media/profile_photos/이순옥.png | Bin 0 -> 51046 bytes media/profile_photos/이승규.png | Bin 0 -> 67202 bytes media/profile_photos/이영희.png | Bin 0 -> 66991 bytes media/profile_photos/이인숙.png | Bin 0 -> 48914 bytes media/profile_photos/이정문.png | Bin 0 -> 57909 bytes media/profile_photos/이주영.png | Bin 0 -> 77608 bytes media/profile_photos/이학민.png | Bin 0 -> 66279 bytes media/profile_photos/이향숙.png | Bin 0 -> 75795 bytes media/profile_photos/이현수.png | Bin 0 -> 78685 bytes media/profile_photos/이현우.png | Bin 0 -> 78630 bytes media/profile_photos/이현욱.png | Bin 0 -> 88993 bytes media/profile_photos/이현정.png | Bin 0 -> 44512 bytes media/profile_photos/이화경.png | Bin 0 -> 56414 bytes media/profile_photos/이효영.png | Bin 0 -> 87746 bytes media/profile_photos/이희태.png | Bin 0 -> 65538 bytes media/profile_photos/임문수.png | Bin 0 -> 45340 bytes media/profile_photos/임영민.png | Bin 0 -> 57843 bytes media/profile_photos/임윤택.png | Bin 0 -> 69793 bytes media/profile_photos/임창섭(수정).png | Bin 0 -> 64588 bytes media/profile_photos/임창섭.png | Bin 0 -> 58479 bytes media/profile_photos/장은화.png | Bin 0 -> 55224 bytes media/profile_photos/장지훈.png | Bin 0 -> 54087 bytes media/profile_photos/장현정.png | Bin 0 -> 71077 bytes media/profile_photos/전미영.png | Bin 0 -> 66977 bytes media/profile_photos/전병웅(수정).png | Bin 0 -> 72043 bytes media/profile_photos/전병웅.png | Bin 0 -> 73314 bytes media/profile_photos/전성훈.png | Bin 0 -> 41791 bytes media/profile_photos/전종태.png | Bin 0 -> 71241 bytes media/profile_photos/전희충.png | Bin 0 -> 75283 bytes media/profile_photos/정석민.png | Bin 0 -> 47500 bytes media/profile_photos/정용표.png | Bin 0 -> 87290 bytes media/profile_photos/정윤목.png | Bin 0 -> 66146 bytes media/profile_photos/정의석.png | Bin 0 -> 80501 bytes media/profile_photos/정종복.png | Bin 0 -> 56532 bytes media/profile_photos/정형재.png | Bin 0 -> 77381 bytes media/profile_photos/제권진.png | Bin 0 -> 72922 bytes media/profile_photos/제오수(수정).png | Bin 0 -> 42001 bytes media/profile_photos/제오수.png | Bin 0 -> 72165 bytes media/profile_photos/제일호.png | Bin 0 -> 50865 bytes media/profile_photos/조승민.png | Bin 0 -> 66586 bytes media/profile_photos/조엘리사.png | Bin 0 -> 68353 bytes media/profile_photos/조정래.png | Bin 0 -> 54425 bytes media/profile_photos/주진우.png | Bin 0 -> 56776 bytes media/profile_photos/주효정.png | Bin 0 -> 42939 bytes media/profile_photos/진서윤.png | Bin 0 -> 61908 bytes media/profile_photos/진종규.png | Bin 0 -> 103777 bytes media/profile_photos/최대경.png | Bin 0 -> 60536 bytes media/profile_photos/최두원.png | Bin 0 -> 64572 bytes media/profile_photos/최승자.png | Bin 0 -> 69635 bytes media/profile_photos/최유심.png | Bin 0 -> 43925 bytes media/profile_photos/최정현(수정).png | Bin 0 -> 78254 bytes media/profile_photos/최정현.png | Bin 0 -> 66711 bytes media/profile_photos/최준익.png | Bin 0 -> 80628 bytes media/profile_photos/최진봉.png | Bin 0 -> 53945 bytes media/profile_photos/하익수.png | Bin 0 -> 87757 bytes media/profile_photos/한윤철.png | Bin 0 -> 57030 bytes media/profile_photos/허남식.png | Bin 0 -> 52570 bytes media/profile_photos/허성우.png | Bin 0 -> 67037 bytes media/profile_photos/현광열.png | Bin 0 -> 55696 bytes media/profile_photos/황미영.png | Bin 0 -> 55929 bytes media/profile_photos/황순민.png | Bin 0 -> 84099 bytes media/profile_photos/황윤미.png | Bin 0 -> 69870 bytes media/profile_photos/황진순.png | Bin 0 -> 63200 bytes media/profile_photos/황태욱.png | Bin 0 -> 62507 bytes media/profile_photos/황하섭.png | Bin 0 -> 73303 bytes media/profile_photos/황현숙.png | Bin 0 -> 57886 bytes media/profile_photos/황현종.png | Bin 0 -> 66279 bytes static/admin/css/custom_admin.css | 18 + staticfiles/account/js/account.js | 20 + staticfiles/account/js/onload.js | 12 + staticfiles/admin/css/autocomplete.css | 275 + staticfiles/admin/css/base.css | 1145 ++ staticfiles/admin/css/changelists.css | 328 + staticfiles/admin/css/custom_admin.css | 18 + staticfiles/admin/css/dark_mode.css | 137 + staticfiles/admin/css/dashboard.css | 29 + staticfiles/admin/css/forms.css | 534 + staticfiles/admin/css/login.css | 61 + staticfiles/admin/css/nav_sidebar.css | 144 + staticfiles/admin/css/responsive.css | 999 ++ staticfiles/admin/css/responsive_rtl.css | 84 + staticfiles/admin/css/rtl.css | 298 + .../css/vendor/select2/LICENSE-SELECT2.md | 21 + .../admin/css/vendor/select2/select2.css | 481 + .../admin/css/vendor/select2/select2.min.css | 1 + staticfiles/admin/css/widgets.css | 604 + staticfiles/admin/img/LICENSE | 20 + staticfiles/admin/img/README.txt | 7 + staticfiles/admin/img/calendar-icons.svg | 14 + staticfiles/admin/img/gis/move_vertex_off.svg | 1 + staticfiles/admin/img/gis/move_vertex_on.svg | 1 + staticfiles/admin/img/icon-addlink.svg | 3 + staticfiles/admin/img/icon-alert.svg | 3 + staticfiles/admin/img/icon-calendar.svg | 9 + staticfiles/admin/img/icon-changelink.svg | 3 + staticfiles/admin/img/icon-clock.svg | 9 + staticfiles/admin/img/icon-deletelink.svg | 3 + staticfiles/admin/img/icon-no.svg | 3 + staticfiles/admin/img/icon-unknown-alt.svg | 3 + staticfiles/admin/img/icon-unknown.svg | 3 + staticfiles/admin/img/icon-viewlink.svg | 3 + staticfiles/admin/img/icon-yes.svg | 3 + staticfiles/admin/img/inline-delete.svg | 3 + staticfiles/admin/img/search.svg | 3 + staticfiles/admin/img/selector-icons.svg | 34 + staticfiles/admin/img/sorting-icons.svg | 19 + staticfiles/admin/img/tooltag-add.svg | 3 + staticfiles/admin/img/tooltag-arrowright.svg | 3 + staticfiles/admin/js/SelectBox.js | 116 + staticfiles/admin/js/SelectFilter2.js | 283 + staticfiles/admin/js/actions.js | 201 + .../admin/js/admin/DateTimeShortcuts.js | 408 + .../admin/js/admin/RelatedObjectLookups.js | 238 + staticfiles/admin/js/autocomplete.js | 33 + staticfiles/admin/js/calendar.js | 221 + staticfiles/admin/js/cancel.js | 29 + staticfiles/admin/js/change_form.js | 16 + staticfiles/admin/js/collapse.js | 43 + staticfiles/admin/js/core.js | 170 + staticfiles/admin/js/filters.js | 30 + staticfiles/admin/js/inlines.js | 359 + staticfiles/admin/js/jquery.init.js | 8 + staticfiles/admin/js/nav_sidebar.js | 79 + staticfiles/admin/js/popup_response.js | 16 + staticfiles/admin/js/prepopulate.js | 43 + staticfiles/admin/js/prepopulate_init.js | 15 + staticfiles/admin/js/theme.js | 56 + staticfiles/admin/js/urlify.js | 169 + .../admin/js/vendor/jquery/LICENSE.txt | 20 + staticfiles/admin/js/vendor/jquery/jquery.js | 10965 ++++++++++++++++ .../admin/js/vendor/jquery/jquery.min.js | 2 + .../admin/js/vendor/select2/LICENSE.md | 21 + .../admin/js/vendor/select2/i18n/af.js | 3 + .../admin/js/vendor/select2/i18n/ar.js | 3 + .../admin/js/vendor/select2/i18n/az.js | 3 + .../admin/js/vendor/select2/i18n/bg.js | 3 + .../admin/js/vendor/select2/i18n/bn.js | 3 + .../admin/js/vendor/select2/i18n/bs.js | 3 + .../admin/js/vendor/select2/i18n/ca.js | 3 + .../admin/js/vendor/select2/i18n/cs.js | 3 + .../admin/js/vendor/select2/i18n/da.js | 3 + .../admin/js/vendor/select2/i18n/de.js | 3 + .../admin/js/vendor/select2/i18n/dsb.js | 3 + .../admin/js/vendor/select2/i18n/el.js | 3 + .../admin/js/vendor/select2/i18n/en.js | 3 + .../admin/js/vendor/select2/i18n/es.js | 3 + .../admin/js/vendor/select2/i18n/et.js | 3 + .../admin/js/vendor/select2/i18n/eu.js | 3 + .../admin/js/vendor/select2/i18n/fa.js | 3 + .../admin/js/vendor/select2/i18n/fi.js | 3 + .../admin/js/vendor/select2/i18n/fr.js | 3 + .../admin/js/vendor/select2/i18n/gl.js | 3 + .../admin/js/vendor/select2/i18n/he.js | 3 + .../admin/js/vendor/select2/i18n/hi.js | 3 + .../admin/js/vendor/select2/i18n/hr.js | 3 + .../admin/js/vendor/select2/i18n/hsb.js | 3 + .../admin/js/vendor/select2/i18n/hu.js | 3 + .../admin/js/vendor/select2/i18n/hy.js | 3 + .../admin/js/vendor/select2/i18n/id.js | 3 + .../admin/js/vendor/select2/i18n/is.js | 3 + .../admin/js/vendor/select2/i18n/it.js | 3 + .../admin/js/vendor/select2/i18n/ja.js | 3 + .../admin/js/vendor/select2/i18n/ka.js | 3 + .../admin/js/vendor/select2/i18n/km.js | 3 + .../admin/js/vendor/select2/i18n/ko.js | 3 + .../admin/js/vendor/select2/i18n/lt.js | 3 + .../admin/js/vendor/select2/i18n/lv.js | 3 + .../admin/js/vendor/select2/i18n/mk.js | 3 + .../admin/js/vendor/select2/i18n/ms.js | 3 + .../admin/js/vendor/select2/i18n/nb.js | 3 + .../admin/js/vendor/select2/i18n/ne.js | 3 + .../admin/js/vendor/select2/i18n/nl.js | 3 + .../admin/js/vendor/select2/i18n/pl.js | 3 + .../admin/js/vendor/select2/i18n/ps.js | 3 + .../admin/js/vendor/select2/i18n/pt-BR.js | 3 + .../admin/js/vendor/select2/i18n/pt.js | 3 + .../admin/js/vendor/select2/i18n/ro.js | 3 + .../admin/js/vendor/select2/i18n/ru.js | 3 + .../admin/js/vendor/select2/i18n/sk.js | 3 + .../admin/js/vendor/select2/i18n/sl.js | 3 + .../admin/js/vendor/select2/i18n/sq.js | 3 + .../admin/js/vendor/select2/i18n/sr-Cyrl.js | 3 + .../admin/js/vendor/select2/i18n/sr.js | 3 + .../admin/js/vendor/select2/i18n/sv.js | 3 + .../admin/js/vendor/select2/i18n/th.js | 3 + .../admin/js/vendor/select2/i18n/tk.js | 3 + .../admin/js/vendor/select2/i18n/tr.js | 3 + .../admin/js/vendor/select2/i18n/uk.js | 3 + .../admin/js/vendor/select2/i18n/vi.js | 3 + .../admin/js/vendor/select2/i18n/zh-CN.js | 3 + .../admin/js/vendor/select2/i18n/zh-TW.js | 3 + .../admin/js/vendor/select2/select2.full.js | 6820 ++++++++++ .../js/vendor/select2/select2.full.min.js | 2 + .../admin/js/vendor/xregexp/LICENSE.txt | 21 + .../admin/js/vendor/xregexp/xregexp.js | 4652 +++++++ .../admin/js/vendor/xregexp/xregexp.min.js | 160 + templates/account/account_inactive.html | 14 + templates/account/base_confirm_code.html | 94 + templates/account/base_entrance.html | 29 + templates/account/base_manage.html | 1 + templates/account/base_manage_email.html | 1 + templates/account/base_manage_password.html | 1 + templates/account/base_manage_phone.html | 1 + templates/account/base_reauthenticate.html | 29 + .../confirm_email_verification_code.html | 17 + templates/account/confirm_login_code.html | 20 + .../account/confirm_password_reset_code.html | 14 + .../confirm_phone_verification_code.html | 17 + templates/account/email.html | 83 + .../email/account_already_exists_message.txt | 13 + .../email/account_already_exists_subject.txt | 4 + templates/account/email/base_message.txt | 7 + templates/account/email/base_notification.txt | 14 + .../account/email/email_changed_message.txt | 4 + .../account/email/email_changed_subject.txt | 4 + .../account/email/email_confirm_message.txt | 4 + .../account/email/email_confirm_subject.txt | 4 + .../email/email_confirmation_message.txt | 9 + .../email_confirmation_signup_message.txt | 1 + .../email_confirmation_signup_subject.txt | 1 + .../email/email_confirmation_subject.txt | 4 + .../account/email/email_deleted_message.txt | 4 + .../account/email/email_deleted_subject.txt | 4 + .../account/email/login_code_message.txt | 9 + .../account/email/login_code_subject.txt | 4 + .../email/password_changed_message.txt | 4 + .../email/password_changed_subject.txt | 4 + .../email/password_reset_code_message.txt | 9 + .../email/password_reset_code_subject.txt | 4 + .../email/password_reset_key_message.txt | 9 + .../email/password_reset_key_subject.txt | 4 + .../account/email/password_reset_message.txt | 4 + .../account/email/password_reset_subject.txt | 4 + .../account/email/password_set_message.txt | 4 + .../account/email/password_set_subject.txt | 4 + .../account/email/unknown_account_message.txt | 10 + .../account/email/unknown_account_subject.txt | 4 + templates/account/email_change.html | 68 + templates/account/email_confirm.html | 39 + templates/account/login(original).html | 61 + templates/account/login.html | 103 + templates/account/logout.html | 64 + .../messages/cannot_delete_primary_email.txt | 2 + .../messages/email_confirmation_failed.txt | 2 + .../messages/email_confirmation_sent.txt | 2 + .../account/messages/email_confirmed.txt | 2 + templates/account/messages/email_deleted.txt | 2 + templates/account/messages/logged_in.txt | 4 + templates/account/messages/logged_out.txt | 2 + .../account/messages/login_code_sent.txt | 2 + .../account/messages/password_changed.txt | 2 + templates/account/messages/password_set.txt | 2 + .../messages/phone_verification_sent.txt | 2 + templates/account/messages/phone_verified.txt | 2 + .../account/messages/primary_email_set.txt | 2 + .../messages/unverified_primary_email.txt | 2 + templates/account/password_change.html | 25 + templates/account/password_reset.html | 68 + templates/account/password_reset_done.html | 54 + .../account/password_reset_from_key.html | 46 + .../account/password_reset_from_key_done.html | 14 + templates/account/password_set.html | 25 + templates/account/phone_change.html | 51 + templates/account/reauthenticate.html | 22 + templates/account/request_login_code.html | 32 + templates/account/signup(original).html | 45 + templates/account/signup.html | 9 + templates/account/signup_by_passkey.html | 38 + templates/account/signup_closed.html | 14 + .../account/snippets/already_logged_in.html | 9 + templates/account/snippets/warn_no_email.html | 4 + templates/account/verification_sent.html | 14 + .../account/verified_email_required.html | 25 + 479 files changed, 36666 insertions(+) create mode 100644 .cursorrules create mode 100644 A_core/__init__.py create mode 100644 A_core/__pycache__/__init__.cpython-38.pyc create mode 100644 A_core/__pycache__/settings.cpython-38.pyc create mode 100644 A_core/__pycache__/urls.cpython-38.pyc create mode 100644 A_core/__pycache__/wsgi.cpython-38.pyc create mode 100644 A_core/asgi.py create mode 100644 A_core/settings.py create mode 100644 A_core/urls.py create mode 100644 A_core/wsgi.py create mode 100644 B_main/__init__.py create mode 100644 B_main/__pycache__/__init__.cpython-38.pyc create mode 100644 B_main/__pycache__/admin.cpython-38.pyc create mode 100644 B_main/__pycache__/apps.cpython-38.pyc create mode 100644 B_main/__pycache__/forms.cpython-38.pyc create mode 100644 B_main/__pycache__/models.cpython-38.pyc create mode 100644 B_main/__pycache__/peopleinfo.cpython-38.pyc create mode 100644 B_main/__pycache__/phonelist.cpython-38.pyc create mode 100644 B_main/__pycache__/urls.cpython-38.pyc create mode 100644 B_main/__pycache__/views.cpython-38.pyc create mode 100644 B_main/admin.py create mode 100644 B_main/apps.py create mode 100644 B_main/clean_duplicates.py create mode 100644 B_main/forms.py create mode 100644 B_main/manual_populate.py create mode 100644 B_main/migrations/0001_initial.py create mode 100644 B_main/migrations/0002_person_user.py create mode 100644 B_main/migrations/0003_person_보일지여부_alter_person_사진.py create mode 100644 B_main/migrations/0004_person_회원가입상태_alter_person_사진.py create mode 100644 B_main/migrations/0005_person_keyword1_person_keyword2_person_keyword3.py create mode 100644 B_main/migrations/0006_person_모든사람보기권한.py create mode 100644 B_main/migrations/0007_remove_person_보일지여부.py create mode 100644 B_main/migrations/0008_remove_person_회원가입상태.py create mode 100644 B_main/migrations/0009_remove_person_keyword2_remove_person_keyword3_and_more.py create mode 100644 B_main/migrations/0010_alter_person_options_person_비밀번호설정필요.py create mode 100644 B_main/migrations/__init__.py create mode 100644 B_main/migrations/__pycache__/0001_initial.cpython-38.pyc create mode 100644 B_main/migrations/__pycache__/0002_person_user.cpython-38.pyc create mode 100644 B_main/migrations/__pycache__/0003_person_보일지여부_alter_person_사진.cpython-38.pyc create mode 100644 B_main/migrations/__pycache__/0004_person_회원가입상태_alter_person_사진.cpython-38.pyc create mode 100644 B_main/migrations/__pycache__/0005_person_keyword1_person_keyword2_person_keyword3.cpython-38.pyc create mode 100644 B_main/migrations/__pycache__/0006_person_모든사람보기권한.cpython-38.pyc create mode 100644 B_main/migrations/__pycache__/0007_remove_person_보일지여부.cpython-38.pyc create mode 100644 B_main/migrations/__pycache__/0008_remove_person_회원가입상태.cpython-38.pyc create mode 100644 B_main/migrations/__pycache__/0009_remove_person_keyword2_remove_person_keyword3_and_more.cpython-38.pyc create mode 100644 B_main/migrations/__pycache__/0010_alter_person_options_person_비밀번호설정필요.cpython-38.pyc create mode 100644 B_main/migrations/__pycache__/__init__.cpython-38.pyc create mode 100644 B_main/models.py create mode 100644 B_main/peopleinfo.py create mode 100644 B_main/restore_phone_dashes.py create mode 100644 B_main/show_all_users.py create mode 100644 B_main/templates/B_main/custom_signup.html create mode 100644 B_main/templates/B_main/main(darkmode).htm create mode 100644 B_main/templates/B_main/main.htm create mode 100644 B_main/templates/B_main/partials/card.htm create mode 100644 B_main/templates/B_main/partials/card_list.htm create mode 100644 B_main/templates/B_main/password.htm create mode 100644 B_main/templates/B_main/phone_verification.html create mode 100644 B_main/templates/B_main/profile_form.htm create mode 100644 B_main/templates/B_main/signup.html create mode 100644 B_main/tests.py create mode 100644 B_main/update_phone_numbers.py create mode 100644 B_main/urls.py create mode 100644 B_main/views.py create mode 100644 C_accounts/__init__.py create mode 100644 C_accounts/__pycache__/__init__.cpython-38.pyc create mode 100644 C_accounts/__pycache__/admin.cpython-38.pyc create mode 100644 C_accounts/__pycache__/apps.cpython-38.pyc create mode 100644 C_accounts/__pycache__/forms.cpython-38.pyc create mode 100644 C_accounts/__pycache__/middleware.cpython-38.pyc create mode 100644 C_accounts/__pycache__/models.cpython-38.pyc create mode 100644 C_accounts/__pycache__/signals.cpython-38.pyc create mode 100644 C_accounts/__pycache__/urls.cpython-38.pyc create mode 100644 C_accounts/__pycache__/views.cpython-38.pyc create mode 100644 C_accounts/admin.py create mode 100644 C_accounts/apps.py create mode 100644 C_accounts/forms.py create mode 100644 C_accounts/middleware.py create mode 100644 C_accounts/migrations/__init__.py create mode 100644 C_accounts/migrations/__pycache__/__init__.cpython-38.pyc create mode 100644 C_accounts/models.py create mode 100644 C_accounts/signals.py create mode 100644 C_accounts/templates/C_accounts/force_password_set.html create mode 100644 C_accounts/templates/C_accounts/password_change.html create mode 100644 C_accounts/templates/C_accounts/password_change_logged_in.html create mode 100644 C_accounts/templates/C_accounts/password_reset.html create mode 100644 C_accounts/templates/C_accounts/profile_edit.html create mode 100644 C_accounts/templates/base.htm create mode 100644 C_accounts/templatetags/__init__.py create mode 100644 C_accounts/templatetags/__pycache__/__init__.cpython-38.pyc create mode 100644 C_accounts/templatetags/__pycache__/form_filters.cpython-38.pyc create mode 100644 C_accounts/templatetags/form_filters.py create mode 100644 C_accounts/tests.py create mode 100644 C_accounts/urls.py create mode 100644 C_accounts/views.py create mode 100644 db.sqlite3 create mode 100644 manage.py create mode 100644 media/profile_photos/Bongsu.jpg create mode 100644 media/profile_photos/bong.jpg create mode 100644 media/profile_photos/default_user.png create mode 100644 media/profile_photos/image.png create mode 100644 media/profile_photos/강경옥.png create mode 100644 media/profile_photos/강규호.png create mode 100644 media/profile_photos/강동혁.png create mode 100644 media/profile_photos/강승구.png create mode 100644 media/profile_photos/강지훈.png create mode 100644 media/profile_photos/고현숙.png create mode 100644 media/profile_photos/공한수.png create mode 100644 media/profile_photos/곽기융.png create mode 100644 media/profile_photos/권중천.png create mode 100644 media/profile_photos/김가현.png create mode 100644 media/profile_photos/김기재.png create mode 100644 media/profile_photos/김기호.png create mode 100644 media/profile_photos/김대성.png create mode 100644 media/profile_photos/김동화.png create mode 100644 media/profile_photos/김미경.png create mode 100644 media/profile_photos/김미애.png create mode 100644 media/profile_photos/김민주.png create mode 100644 media/profile_photos/김보성.png create mode 100644 media/profile_photos/김봉수.png create mode 100644 media/profile_photos/김상우.png create mode 100644 media/profile_photos/김상준.png create mode 100644 media/profile_photos/김선이.png create mode 100644 media/profile_photos/김성주.png create mode 100644 media/profile_photos/김성훈.png create mode 100644 media/profile_photos/김영하.png create mode 100644 media/profile_photos/김영훈.png create mode 100644 media/profile_photos/김외숙.png create mode 100644 media/profile_photos/김용권.png create mode 100644 media/profile_photos/김윤규.png create mode 100644 media/profile_photos/김윤아.png create mode 100644 media/profile_photos/김은희.png create mode 100644 media/profile_photos/김일곤.png create mode 100644 media/profile_photos/김재준.png create mode 100644 media/profile_photos/김재홍.png create mode 100644 media/profile_photos/김정호.png create mode 100644 media/profile_photos/김준수.png create mode 100644 media/profile_photos/김중선.png create mode 100644 media/profile_photos/김진홍.png create mode 100644 media/profile_photos/김태영.png create mode 100644 media/profile_photos/김태형.png create mode 100644 media/profile_photos/김한집.png create mode 100644 media/profile_photos/김현우.png create mode 100644 media/profile_photos/김현준.png create mode 100644 media/profile_photos/김희경(수정).png create mode 100644 media/profile_photos/김희경.png create mode 100644 media/profile_photos/노현주.png create mode 100644 media/profile_photos/노희숙.png create mode 100644 media/profile_photos/마점래(수정).png create mode 100644 media/profile_photos/마점래.png create mode 100644 media/profile_photos/문성배.png create mode 100644 media/profile_photos/문정순.png create mode 100644 media/profile_photos/민수연.png create mode 100644 media/profile_photos/민홍기.png create mode 100644 media/profile_photos/박강범.png create mode 100644 media/profile_photos/박경민.png create mode 100644 media/profile_photos/박국제.png create mode 100644 media/profile_photos/박대진.png create mode 100644 media/profile_photos/박명숙.png create mode 100644 media/profile_photos/박명옥.png create mode 100644 media/profile_photos/박명진.png create mode 100644 media/profile_photos/박민희.png create mode 100644 media/profile_photos/박부술.png create mode 100644 media/profile_photos/박성호.png create mode 100644 media/profile_photos/박성훈.png create mode 100644 media/profile_photos/박순자.png create mode 100644 media/profile_photos/박영우.png create mode 100644 media/profile_photos/박영해.png create mode 100644 media/profile_photos/박재성.png create mode 100644 media/profile_photos/박정숙.png create mode 100644 media/profile_photos/박정은.png create mode 100644 media/profile_photos/박종인.png create mode 100644 media/profile_photos/박주원(수정).png create mode 100644 media/profile_photos/박주원.png create mode 100644 media/profile_photos/박지환.png create mode 100644 media/profile_photos/배기중.png create mode 100644 media/profile_photos/배범한.png create mode 100644 media/profile_photos/배성효.png create mode 100644 media/profile_photos/변연옥.png create mode 100644 media/profile_photos/빈윤진.png create mode 100644 media/profile_photos/서강섭.png create mode 100644 media/profile_photos/서지윤.png create mode 100644 media/profile_photos/성동화.png create mode 100644 media/profile_photos/성충식.png create mode 100644 media/profile_photos/손동현.png create mode 100644 media/profile_photos/송연익.png create mode 100644 media/profile_photos/심수현.png create mode 100644 media/profile_photos/안복지.png create mode 100644 media/profile_photos/안상배.png create mode 100644 media/profile_photos/안영봉.png create mode 100644 media/profile_photos/양재진.png create mode 100644 media/profile_photos/어익수.png create mode 100644 media/profile_photos/엄신아.png create mode 100644 media/profile_photos/여은주.png create mode 100644 media/profile_photos/예영숙.png create mode 100644 media/profile_photos/오용택.png create mode 100644 media/profile_photos/오원재.png create mode 100644 media/profile_photos/유석찬.png create mode 100644 media/profile_photos/이기영.png create mode 100644 media/profile_photos/이대명.png create mode 100644 media/profile_photos/이두홍.png create mode 100644 media/profile_photos/이범민.png create mode 100644 media/profile_photos/이상경.png create mode 100644 media/profile_photos/이상규.png create mode 100644 media/profile_photos/이상민.png create mode 100644 media/profile_photos/이수연.png create mode 100644 media/profile_photos/이순옥.png create mode 100644 media/profile_photos/이승규.png create mode 100644 media/profile_photos/이영희.png create mode 100644 media/profile_photos/이인숙.png create mode 100644 media/profile_photos/이정문.png create mode 100644 media/profile_photos/이주영.png create mode 100644 media/profile_photos/이학민.png create mode 100644 media/profile_photos/이향숙.png create mode 100644 media/profile_photos/이현수.png create mode 100644 media/profile_photos/이현우.png create mode 100644 media/profile_photos/이현욱.png create mode 100644 media/profile_photos/이현정.png create mode 100644 media/profile_photos/이화경.png create mode 100644 media/profile_photos/이효영.png create mode 100644 media/profile_photos/이희태.png create mode 100644 media/profile_photos/임문수.png create mode 100644 media/profile_photos/임영민.png create mode 100644 media/profile_photos/임윤택.png create mode 100644 media/profile_photos/임창섭(수정).png create mode 100644 media/profile_photos/임창섭.png create mode 100644 media/profile_photos/장은화.png create mode 100644 media/profile_photos/장지훈.png create mode 100644 media/profile_photos/장현정.png create mode 100644 media/profile_photos/전미영.png create mode 100644 media/profile_photos/전병웅(수정).png create mode 100644 media/profile_photos/전병웅.png create mode 100644 media/profile_photos/전성훈.png create mode 100644 media/profile_photos/전종태.png create mode 100644 media/profile_photos/전희충.png create mode 100644 media/profile_photos/정석민.png create mode 100644 media/profile_photos/정용표.png create mode 100644 media/profile_photos/정윤목.png create mode 100644 media/profile_photos/정의석.png create mode 100644 media/profile_photos/정종복.png create mode 100644 media/profile_photos/정형재.png create mode 100644 media/profile_photos/제권진.png create mode 100644 media/profile_photos/제오수(수정).png create mode 100644 media/profile_photos/제오수.png create mode 100644 media/profile_photos/제일호.png create mode 100644 media/profile_photos/조승민.png create mode 100644 media/profile_photos/조엘리사.png create mode 100644 media/profile_photos/조정래.png create mode 100644 media/profile_photos/주진우.png create mode 100644 media/profile_photos/주효정.png create mode 100644 media/profile_photos/진서윤.png create mode 100644 media/profile_photos/진종규.png create mode 100644 media/profile_photos/최대경.png create mode 100644 media/profile_photos/최두원.png create mode 100644 media/profile_photos/최승자.png create mode 100644 media/profile_photos/최유심.png create mode 100644 media/profile_photos/최정현(수정).png create mode 100644 media/profile_photos/최정현.png create mode 100644 media/profile_photos/최준익.png create mode 100644 media/profile_photos/최진봉.png create mode 100644 media/profile_photos/하익수.png create mode 100644 media/profile_photos/한윤철.png create mode 100644 media/profile_photos/허남식.png create mode 100644 media/profile_photos/허성우.png create mode 100644 media/profile_photos/현광열.png create mode 100644 media/profile_photos/황미영.png create mode 100644 media/profile_photos/황순민.png create mode 100644 media/profile_photos/황윤미.png create mode 100644 media/profile_photos/황진순.png create mode 100644 media/profile_photos/황태욱.png create mode 100644 media/profile_photos/황하섭.png create mode 100644 media/profile_photos/황현숙.png create mode 100644 media/profile_photos/황현종.png create mode 100644 static/admin/css/custom_admin.css create mode 100644 staticfiles/account/js/account.js create mode 100644 staticfiles/account/js/onload.js create mode 100644 staticfiles/admin/css/autocomplete.css create mode 100644 staticfiles/admin/css/base.css create mode 100644 staticfiles/admin/css/changelists.css create mode 100644 staticfiles/admin/css/custom_admin.css create mode 100644 staticfiles/admin/css/dark_mode.css create mode 100644 staticfiles/admin/css/dashboard.css create mode 100644 staticfiles/admin/css/forms.css create mode 100644 staticfiles/admin/css/login.css create mode 100644 staticfiles/admin/css/nav_sidebar.css create mode 100644 staticfiles/admin/css/responsive.css create mode 100644 staticfiles/admin/css/responsive_rtl.css create mode 100644 staticfiles/admin/css/rtl.css create mode 100644 staticfiles/admin/css/vendor/select2/LICENSE-SELECT2.md create mode 100644 staticfiles/admin/css/vendor/select2/select2.css create mode 100644 staticfiles/admin/css/vendor/select2/select2.min.css create mode 100644 staticfiles/admin/css/widgets.css create mode 100644 staticfiles/admin/img/LICENSE create mode 100644 staticfiles/admin/img/README.txt create mode 100644 staticfiles/admin/img/calendar-icons.svg create mode 100644 staticfiles/admin/img/gis/move_vertex_off.svg create mode 100644 staticfiles/admin/img/gis/move_vertex_on.svg create mode 100644 staticfiles/admin/img/icon-addlink.svg create mode 100644 staticfiles/admin/img/icon-alert.svg create mode 100644 staticfiles/admin/img/icon-calendar.svg create mode 100644 staticfiles/admin/img/icon-changelink.svg create mode 100644 staticfiles/admin/img/icon-clock.svg create mode 100644 staticfiles/admin/img/icon-deletelink.svg create mode 100644 staticfiles/admin/img/icon-no.svg create mode 100644 staticfiles/admin/img/icon-unknown-alt.svg create mode 100644 staticfiles/admin/img/icon-unknown.svg create mode 100644 staticfiles/admin/img/icon-viewlink.svg create mode 100644 staticfiles/admin/img/icon-yes.svg create mode 100644 staticfiles/admin/img/inline-delete.svg create mode 100644 staticfiles/admin/img/search.svg create mode 100644 staticfiles/admin/img/selector-icons.svg create mode 100644 staticfiles/admin/img/sorting-icons.svg create mode 100644 staticfiles/admin/img/tooltag-add.svg create mode 100644 staticfiles/admin/img/tooltag-arrowright.svg create mode 100644 staticfiles/admin/js/SelectBox.js create mode 100644 staticfiles/admin/js/SelectFilter2.js create mode 100644 staticfiles/admin/js/actions.js create mode 100644 staticfiles/admin/js/admin/DateTimeShortcuts.js create mode 100644 staticfiles/admin/js/admin/RelatedObjectLookups.js create mode 100644 staticfiles/admin/js/autocomplete.js create mode 100644 staticfiles/admin/js/calendar.js create mode 100644 staticfiles/admin/js/cancel.js create mode 100644 staticfiles/admin/js/change_form.js create mode 100644 staticfiles/admin/js/collapse.js create mode 100644 staticfiles/admin/js/core.js create mode 100644 staticfiles/admin/js/filters.js create mode 100644 staticfiles/admin/js/inlines.js create mode 100644 staticfiles/admin/js/jquery.init.js create mode 100644 staticfiles/admin/js/nav_sidebar.js create mode 100644 staticfiles/admin/js/popup_response.js create mode 100644 staticfiles/admin/js/prepopulate.js create mode 100644 staticfiles/admin/js/prepopulate_init.js create mode 100644 staticfiles/admin/js/theme.js create mode 100644 staticfiles/admin/js/urlify.js create mode 100644 staticfiles/admin/js/vendor/jquery/LICENSE.txt create mode 100644 staticfiles/admin/js/vendor/jquery/jquery.js create mode 100644 staticfiles/admin/js/vendor/jquery/jquery.min.js create mode 100644 staticfiles/admin/js/vendor/select2/LICENSE.md create mode 100644 staticfiles/admin/js/vendor/select2/i18n/af.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ar.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/az.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/bg.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/bn.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/bs.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ca.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/cs.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/da.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/de.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/dsb.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/el.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/en.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/es.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/et.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/eu.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/fa.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/fi.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/fr.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/gl.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/he.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hi.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hr.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hsb.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hu.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/hy.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/id.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/is.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/it.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ja.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ka.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/km.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ko.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/lt.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/lv.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/mk.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ms.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/nb.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ne.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/nl.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/pl.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ps.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/pt-BR.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/pt.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ro.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/ru.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sk.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sl.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sq.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sr-Cyrl.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sr.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/sv.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/th.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/tk.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/tr.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/uk.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/vi.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/zh-CN.js create mode 100644 staticfiles/admin/js/vendor/select2/i18n/zh-TW.js create mode 100644 staticfiles/admin/js/vendor/select2/select2.full.js create mode 100644 staticfiles/admin/js/vendor/select2/select2.full.min.js create mode 100644 staticfiles/admin/js/vendor/xregexp/LICENSE.txt create mode 100644 staticfiles/admin/js/vendor/xregexp/xregexp.js create mode 100644 staticfiles/admin/js/vendor/xregexp/xregexp.min.js create mode 100644 templates/account/account_inactive.html create mode 100644 templates/account/base_confirm_code.html create mode 100644 templates/account/base_entrance.html create mode 100644 templates/account/base_manage.html create mode 100644 templates/account/base_manage_email.html create mode 100644 templates/account/base_manage_password.html create mode 100644 templates/account/base_manage_phone.html create mode 100644 templates/account/base_reauthenticate.html create mode 100644 templates/account/confirm_email_verification_code.html create mode 100644 templates/account/confirm_login_code.html create mode 100644 templates/account/confirm_password_reset_code.html create mode 100644 templates/account/confirm_phone_verification_code.html create mode 100644 templates/account/email.html create mode 100644 templates/account/email/account_already_exists_message.txt create mode 100644 templates/account/email/account_already_exists_subject.txt create mode 100644 templates/account/email/base_message.txt create mode 100644 templates/account/email/base_notification.txt create mode 100644 templates/account/email/email_changed_message.txt create mode 100644 templates/account/email/email_changed_subject.txt create mode 100644 templates/account/email/email_confirm_message.txt create mode 100644 templates/account/email/email_confirm_subject.txt create mode 100644 templates/account/email/email_confirmation_message.txt create mode 100644 templates/account/email/email_confirmation_signup_message.txt create mode 100644 templates/account/email/email_confirmation_signup_subject.txt create mode 100644 templates/account/email/email_confirmation_subject.txt create mode 100644 templates/account/email/email_deleted_message.txt create mode 100644 templates/account/email/email_deleted_subject.txt create mode 100644 templates/account/email/login_code_message.txt create mode 100644 templates/account/email/login_code_subject.txt create mode 100644 templates/account/email/password_changed_message.txt create mode 100644 templates/account/email/password_changed_subject.txt create mode 100644 templates/account/email/password_reset_code_message.txt create mode 100644 templates/account/email/password_reset_code_subject.txt create mode 100644 templates/account/email/password_reset_key_message.txt create mode 100644 templates/account/email/password_reset_key_subject.txt create mode 100644 templates/account/email/password_reset_message.txt create mode 100644 templates/account/email/password_reset_subject.txt create mode 100644 templates/account/email/password_set_message.txt create mode 100644 templates/account/email/password_set_subject.txt create mode 100644 templates/account/email/unknown_account_message.txt create mode 100644 templates/account/email/unknown_account_subject.txt create mode 100644 templates/account/email_change.html create mode 100644 templates/account/email_confirm.html create mode 100644 templates/account/login(original).html create mode 100644 templates/account/login.html create mode 100644 templates/account/logout.html create mode 100644 templates/account/messages/cannot_delete_primary_email.txt create mode 100644 templates/account/messages/email_confirmation_failed.txt create mode 100644 templates/account/messages/email_confirmation_sent.txt create mode 100644 templates/account/messages/email_confirmed.txt create mode 100644 templates/account/messages/email_deleted.txt create mode 100644 templates/account/messages/logged_in.txt create mode 100644 templates/account/messages/logged_out.txt create mode 100644 templates/account/messages/login_code_sent.txt create mode 100644 templates/account/messages/password_changed.txt create mode 100644 templates/account/messages/password_set.txt create mode 100644 templates/account/messages/phone_verification_sent.txt create mode 100644 templates/account/messages/phone_verified.txt create mode 100644 templates/account/messages/primary_email_set.txt create mode 100644 templates/account/messages/unverified_primary_email.txt create mode 100644 templates/account/password_change.html create mode 100644 templates/account/password_reset.html create mode 100644 templates/account/password_reset_done.html create mode 100644 templates/account/password_reset_from_key.html create mode 100644 templates/account/password_reset_from_key_done.html create mode 100644 templates/account/password_set.html create mode 100644 templates/account/phone_change.html create mode 100644 templates/account/reauthenticate.html create mode 100644 templates/account/request_login_code.html create mode 100644 templates/account/signup(original).html create mode 100644 templates/account/signup.html create mode 100644 templates/account/signup_by_passkey.html create mode 100644 templates/account/signup_closed.html create mode 100644 templates/account/snippets/already_logged_in.html create mode 100644 templates/account/snippets/warn_no_email.html create mode 100644 templates/account/verification_sent.html create mode 100644 templates/account/verified_email_required.html diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..2f9677f --- /dev/null +++ b/.cursorrules @@ -0,0 +1,16 @@ +# Cursor AI 설정 규칙 + +## 터미널 명령어 자동 실행 +- 사용자가 conda activate, python manage.py, pip install 등의 터미널 명령어를 요청하면 자동으로 실행 +- 별도 확인 없이 즉시 명령어 실행 +- Django 프로젝트 관련 명령어 (makemigrations, migrate, runserver 등) 자동 실행 + +## 프로젝트 컨텍스트 +- Django 웹 애플리케이션 (SillaAMP_user) +- Python 3.8 환경 사용 +- 현재 작업 디렉토리: C:\Users\CPABONG\Desktop\SillaAMP_user + +## 코딩 스타일 +- Python/Django 코드 작성 시 PEP 8 준수 +- 한글 주석 사용 가능 +- 명확하고 읽기 쉬운 코드 작성 \ No newline at end of file diff --git a/A_core/__init__.py b/A_core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/A_core/__pycache__/__init__.cpython-38.pyc b/A_core/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e52d853043318a3ad249bb5780e619499254e28 GIT binary patch literal 148 zcmWIL<>g`kg4Yh+86f&Gh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6w#*(xTqIJKxa z#yP;z$=}aC#wE2lyClCLCO9)EC(+S2AifkR9pf0EoL`h06Ca2 QKczG$)edCjXCP((0M*PQc>n+a literal 0 HcmV?d00001 diff --git a/A_core/__pycache__/settings.cpython-38.pyc b/A_core/__pycache__/settings.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34c35e18d16159b26a0632a7d7c65887390c9a6d GIT binary patch literal 3104 zcmb7GOLN;s5+)#sq$tUj<%eu5PN>+9En5QR_?37QuOI@FSs@8V0PSQ-6%>RXQIPPU zF<`vHb?^HNa@eiPC4ULm-HZRgZdJ+^ZvzAMNfD+<2?SE^ z703)sLlH_)h8d{9EX=_?oP+bQ(3>L_xIkuM5ia(4G6$F7GF*WrxC+OX0QX@H zW$wdzI*RF7v$>u?7GZ;2ga_mjdU+XtSKxE9gxU|63*;)kA0^hvHF!*}-kki!_fA6ta{)FRmrgY zfL8l~e?+ZNtWV8TIly}i`8<6 zYeV?(kX8={j#G7P7FPX((+!y(pdo4p)gMh~K-vFeE1(BshTbMgw#vmq#|#gntt1?iz!!# zRwryHsN%k99a9gO*z$ooTfDJ=-TMH^fWF4A5=Qqk*-8zx7^oH8jqNOZwZ&=S3&3+#bGcAp{ z1oXfhIN_-9VgEFa?6rd>{xGT_F`KXQaZzdSG}=lmw5663B)4kxV#4n~|Mgu8KdfFhQn(f+-nN;1&p@bhq&p#BYWqYp5fBsPYVKwGyb_%fh zp#bfHO9R_Fi!e>S^#O~gx_Uh-;XR0Ls20x-Cut)k#o;0<)m#d;StHZwJGznC#&Eky zw`1a#-NVxMw{7Y`RIX)czjC!`p_Vt#n&bCuuNIdF7+eqQa=X2HGcFkhUYLdvm-=`u zIre_Q<4MJ3!$^Ec=mBNE^COLmee8r@NXlE1rX+b2S2U%*tLVlz%J=cKtZa36;(4jr zR9`8w@j}&fEk4(1YdVsUfXSwjlGe}_qanvjQdfVWwDm?^N;1#LGE<8yt%fW&l~>ZP z63^|bs%~_5n{~Cl9na`WtJ9QpMT-|+X*-Psv6<<|GqR*hiDO!PHGwudlBT^^9gt3bXyGlLX8(Eg> zb+y~p4dkg^{rAipIddXjEk%E!%H&$kMz431Iz#PmyNE55D>+MPNsZH8rfdGBWc8PbKubD$*s+P#zB~yn_4IdQvxrPcy9De@l)wMp}iHZ3(4v?p*Z5_lK&Cr z1cB$u9M6w<@A&upJK?>sFhnf=hVyy8fK=hk_;2ANT27v#z?J#pdqKeWH2O0wa5!?w F^Iu~~3R3_8 literal 0 HcmV?d00001 diff --git a/A_core/__pycache__/urls.cpython-38.pyc b/A_core/__pycache__/urls.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a321963da3865251024a0e54e2458abe8e1ca750 GIT binary patch literal 1249 zcmb7E&u`N(6i(7~ZPSeX5SrlTTNi00VuDNgA-cj~5{zkM9HL5D7Q1aqVn?>aSR_t6 zkN<;zDObdSe_K$m->lYVvXZv^maY*KH;FN8&pKBI*ej$pvv3Qyap&5= z7Vcg*z-yBxUcJ(*2xZ@@?KQC1l|5fv0uX^DLmy*uAzZW%2&p_TvsYj^yz}@vE%i>6QD%gYc!n0m{3RA!m zeQQ6bQN_4QTY=tp(Ewu*sIrYt2ph=wV7(o=TNv4%+~#JzJU3@@SnjRlia!W9v>V!WX93b6i%jf!_e5h##t z=QlZxh#Yz?D6?eiY zVmXz<%Rv4M#<9nVAkr)DdVg#M^R0b=Z$Lq1CnInrDWS65s)Oet!2%5OnySl1=NDLE zhe;CVa$0dO)`r1it8$cD(cKHuFs9Y=(6P%_5Wvk20{>PmCiFn^_?*L|)5`9W!n^?< z4xb9mzg@el>)}oaB|MIJQ=KqHEJezw6!tl36AlUth}ml-xI+k*3RMkFiBi#|(ltVgPIAoo<>KJ8PiH5kT^X6# z>Bt{q@Y<>3A28v|2`NwVvz|ZueV;#jdTE6& zj%6?l{M?ebXw6yyf?(DDxyRdl?-u*2^$pGTIZjY-|K@l2DH)DPrqx2^WT7k4ru3YD8)$8{+63Mh408~3!)5!DVwb=L}e9@hc25tUXh^anXC*jifmaEI_0 zs_JLlroV4b4%4ZD%A_Y3{r8jcFdcyTVs)7&!n@i(zo2Umr;V1hrisdH*L~O6eB{3Y PGF_kA54!jfevJPCBmb&P literal 0 HcmV?d00001 diff --git a/A_core/asgi.py b/A_core/asgi.py new file mode 100644 index 0000000..6aaec3a --- /dev/null +++ b/A_core/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for A_core project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'A_core.settings') + +application = get_asgi_application() diff --git a/A_core/settings.py b/A_core/settings.py new file mode 100644 index 0000000..165b793 --- /dev/null +++ b/A_core/settings.py @@ -0,0 +1,172 @@ +""" +Django settings for A_core project. + +Generated by 'django-admin startproject' using Django 4.2.16. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.2/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-kst@+h&50%!m$(d!l*qbb0l7f@z#@#me__yye^$5kg%0m%1=im' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + # Django 기본 앱들 + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + + # 필수: django.contrib.sites + 'django.contrib.sites', + + # allauth 관련 앱들 (순서 중요) + 'allauth', + 'allauth.account', + 'allauth.socialaccount', + + # 소셜 로그인 제공자 (필요한 경우 추가) + # 'allauth.socialaccount.providers.google', + + + # 프로젝트 앱들 + 'B_main', + 'C_accounts', +] + +SITE_ID = 1 + +AUTHENTICATION_BACKENDS = [ + 'django.contrib.auth.backends.ModelBackend', + 'allauth.account.auth_backends.AuthenticationBackend', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'allauth.account.middleware.AccountMiddleware', + 'C_accounts.middleware.ForcePasswordSetMiddleware', # 강제 비밀번호 설정 미들웨어 +] + +ROOT_URLCONF = 'A_core.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [BASE_DIR / 'templates'], # 이 줄이 반드시 있어야 함 + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'A_core.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.2/howto/static-files/ + +STATIC_URL = 'static/' +STATIC_ROOT = BASE_DIR / 'staticfiles' +STATICFILES_DIRS = [ + BASE_DIR / 'static', +] + +MEDIA_URL = '/media/' +MEDIA_ROOT = BASE_DIR / 'media' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + +STATIC_URL = '/static/' + + + +# 로그인/회원가입 redirect 설정 +LOGIN_REDIRECT_URL = '/' +ACCOUNT_LOGOUT_REDIRECT_URL = '/accounts/login/' + +# 전화번호로 로그인 (username 사용) +ACCOUNT_AUTHENTICATION_METHOD = 'username' # 'email' → 'username' +ACCOUNT_USERNAME_REQUIRED = True # username 필드 사용 (전화번호) +ACCOUNT_EMAIL_REQUIRED = False # email 필수 아님 +ACCOUNT_USER_MODEL_USERNAME_FIELD = 'username' # 사용자 모델의 username 필드 활성화 + diff --git a/A_core/urls.py b/A_core/urls.py new file mode 100644 index 0000000..e50d00a --- /dev/null +++ b/A_core/urls.py @@ -0,0 +1,29 @@ +""" +URL configuration for A_core project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include +from django.views.generic import RedirectView + + +urlpatterns = [ + path('admin/', admin.site.urls), + # allauth 비밀번호 재설정을 커스텀 시스템으로 리다이렉트 + path('accounts/password/reset/', RedirectView.as_view(url='/accounts/password_reset/', permanent=False), name='account_reset_password'), + path('accounts/', include('allauth.urls')), # allauth 기본 URL + path('accounts/', include('C_accounts.urls')), # 커스텀 계정 URL + path('', include('B_main.urls')), +] diff --git a/A_core/wsgi.py b/A_core/wsgi.py new file mode 100644 index 0000000..0e689b4 --- /dev/null +++ b/A_core/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for A_core project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'A_core.settings') + +application = get_wsgi_application() diff --git a/B_main/__init__.py b/B_main/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/B_main/__pycache__/__init__.cpython-38.pyc b/B_main/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a77d467cde196189d595a2ba2f01d20b18229f62 GIT binary patch literal 148 zcmWIL<>g`kg0BwU86f&Gh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6w#*(xTqIJKxa z#yP;z$=}aC#wE2lyClCLCO9)EC(+S2AifkR9pe2 QKczG$)edCjXCP((0NUarfB*mh literal 0 HcmV?d00001 diff --git a/B_main/__pycache__/admin.cpython-38.pyc b/B_main/__pycache__/admin.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff21fd54a343e78652cea5d81493a4b17738038a GIT binary patch literal 3733 zcmb_f|8E?{72nz2yW2aT?bvY=2#_p>77mCVN};VCQafpaK;#-^^2^E6YJD@lTW9YJ zGrLXfwi48r2&tw}k(4yVX%L9i6;e}(Dp#Wa$o$y;L~gBCRni~&L(}(Wea^-{qe|_b z_xa76**9-K^O-j@r+RyH8a&q6FRJ5tP5Ubrl1~Z>6Yz=|5LBbY*J`9h2( zbi+4msZt7blcxN1EmO)6?In$-X{M#o3`@;fi$*ES$h92Hd_zr}dNSdP86}gFQi|#R>n)iQ!Mx8FzBkk0l<_h9+2&bW zG<=WRkCn^ial7eK>eVY__VdlN<96KY0C%YuiZS~+uqn8LfQw=wD=o*VyEW!GGUqt8 z1`U1G^N#aI==yOD$IfL6Dw!|SvmW!Q;5bg1J?{DJ<$5y=Waf-VD=ZKz8s7<3O8o1E zqhr&j1YkaW^yK7`9~NJl{s9xGgGO_D%JY49^2A9egzD)dPR;e|(`pV!n)A}c!DqmY z*a5;KCNXtgGl{P0chh(IE-)4gCI<#;ycb0u3N+vAJl2NLhMsT?LI+_HRo*gjS*V-9 zM*jfO;XpibRL3c!DWoS7v66+UL=CJJvy|Sb&s9>T4DF%$iVhP?Dx{R9I>?-oJ+znh z(SDE2>U0Na148={H)Ww3B|5J^}V2dVoGj3t-trN9a@Z zY0!4l$AE&PvL``7X-yq_?bNa2(PJ_7mT$H{ZN=*OrRe-Sv3mWj=nv=aAacd8 zjy}I6O`Op?4}h)xyWh6ozXq&rUtMZ{ar4&djaz?QTDy29uKZK0edEpc=WnlFT8&zt zMAt5^U2H{{FL3lGtKYT>7C}W*>hUonMMOUR-XUYegSk zfWAWv47N zVHh}i3l^Gwmp=|_8RP3bsAHNu3a>y+e?sOoJ`@m2P@97!IR&W_h-4HOUOVK~Dz@O| z7l(iR(_vdBtQUuC?%4y7C4wsCtb+$JIiaP>yh=6LFm1|Ln8Swc(Zlh)scE;P56(x+ zO9kV$nk{AH7@9Of?yu<56wIHMMq_3!F<^xFc6aWK;*gMM8c7o!e(8UkW{6Xm#J&sX zFwwv^XT1ZoLCqx#2e;$yYdI_3`1u zf4TC$9j_}pURZX_|3jnr(cy4no9}1*kiqAF|IIHyvDYpyMIXL*>&BbW)pOC+_uAK& z?srnqZ-3qLRao;UprcLj@~6S}U3e9d5KK!Ckw{4xw@V6Yg5$B+ahmvFU_1i7c7u^K ziXdLT_P5n{?RFvs*>MiC<0RBUb8;Iu_svEZFVU}C?T?q%y>#GAw7W0iA$7aL`3zKi zAKtHpa~rQAQmxwnA@{8%Lqs3L46}K3ksc>R!DJWJ(RpB91%7d$9-qT z;CSwm8P47anFx3RN}FgZcU4{Cz*&aD5a7F-Cxq9izXC1SBV2ei8D7`c+XI*klVfV( z2SEUE7#M#J#l``NV^)%b*aIBe62Y7RdncHEn}fN(>3;{p8q=k*dAXE((1Y(;=4ok1l}F+zNF6H(&DTFGXQYs iRE@e$sZP6%Q(A|>8C*;bV=hoTbsh!^KYcJ~k^cY)?K0v3 literal 0 HcmV?d00001 diff --git a/B_main/__pycache__/apps.cpython-38.pyc b/B_main/__pycache__/apps.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e7b0059d8763865af84da212e75b1b2209787b1 GIT binary patch literal 424 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 FzX7Bd-%9`h literal 0 HcmV?d00001 diff --git a/B_main/__pycache__/models.cpython-38.pyc b/B_main/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..102cc08d7d278a8767beb1e43fce56a267a89ca3 GIT binary patch literal 2289 zcmZuz&2JM&6yII1?RDZfAp{74mOWLqR5@xdtx#1FOh8D*q)O;v6=}8c46(!7Yxbjs zh(k&o6i7m;Lewb1kx>I76$dxaHb~GvWRC4A-tn!cUfMUaHn@SY=Ixt#Z{GXOdv9iZ zsjI6)fVT44-P{LJLHLUe+FKX~r=UAu13>~3O+jN{$rKBcCW(v>nW2KL$()zXa3P{a zL;*|Z1QbS*vVbBuIvbi5H3ep(NP!sz%Tux0kk*03Q6U-I1KUC-DUxo4vsTe@Z7=KQ zCrk`ek%EP==>R8Ra5G|j<7cpQ#kES~qj}RbhAxcg z9*D;S^W(vuQ>E)f(e|zS!)@BV=pgD_3TsE2I)~6Z7RbkJ57Wx7THkp^ z*J|qj)M=#*D_*K}epRI#^Yv%dkJPh<>9j9stS!>@d-d83-IxQLO-KF~^YwZqz4NQT zFz+vHrU3C#@Jiy}V0*%HFfUjV%VD#myZD;x^?gw_6| z-Io33O81Nqfl#MyR`lzw8O3S-+?Y{aUU7bIv z0{r^3a=msB0+dXK_l46Q0LL~9qsKV^McJ>-_&f8BRRB`nq}3I*u~G(W-c=Cq3nf^j zYuj{#1xNL_??aRucUJ*6-I`Hp^@sZJ0BYfZ`cA7YMgJivJbntBhlPIicH_>je{Z`P z%(srG2q;cL-*+RzZ76ASa3fB41_hnEfL+652R7g^jL}s@!iKTOf9f;otvCHAyTBNs@NbHQ^1}j|_B05{~=gM>)^7}GS+xm|F zGUxJm{#f%XujtNdAo%Gey|qX>_+!Lv2h&g|TV*Xgc7E*3VG=)Ibnzq(d=q7K_^V69 znKaCwwk#7H#b8}OI1Y|#kiFns2gh1duHft6S~mLFeiCy^|N^rI=GIBBIkH*Y$rwm02o4;YCx z#c0CjnHXq};punn{6xy|+*~Sn0)w)&L Ml>o3LiM`?f0IF%;X8-^I literal 0 HcmV?d00001 diff --git a/B_main/__pycache__/peopleinfo.cpython-38.pyc b/B_main/__pycache__/peopleinfo.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09036ecbc376b0dfa9af8bdc55e9b1e9e7871953 GIT binary patch literal 30496 zcmai-2VfjWxyNmZ5(1%w-og6@WAUf?OC?$=kg`_&op1ohHm=fgO)8b%gl1qVLHt{%stJ$%)QM?<~|em zF(;e*n){hk%>B&+%mdAXChl!cH4ioq;mAYH!_33=$Ro@n&7(N-X!98JSUqx@d7R0Z z$D7m56U-CMlia<|Fi$pJW`%i*d8%2d*JaHrb0)vcnY{7zNWm11Z%U?Y0#h+hGf!6( zXPIZ1XPRf3v&}hXwO)6wInS)&UgvY~XX}yYm1H8uNPd z26L@>qq)vpuSae$Z!$M>%}wUbrW*C|7V}o~Hb&s>=4SH_^G?^pyUe@IdrY@kXV#ky z`s*IkYc}%h_nJO)ON4rp={Ex$-E7`xwwOWp>#gSf<^voZGQ;Lp{q=|$HDeqZH`~mF z9+@=T&247N>@Yjc?RsR|>@vH#W{;UMd!ruiFdsCt9NovgKcv^~H#KvBBL~ePGZ$TV z*c>r;a&+DtH6MUys~v{>%Kp{LuW!{Mh^?y4O$5&&r59YtkAKke9$^6;;1)ln=`J4HBG@Ab~ z|1|$H|2F?){@46Rem%I{EQ6Lq9Z)B959prIz1(@Z<|ODo(8gfF7B+;!%8mH1rr#;`6c4Y0%@K4D@(e5BxnH zdII!Bz0Z?qodG>LuYwOehECArC4*MaZ{fM@vwYp}<_r zX9ap1^mO;cXVHEJ^i1vLS+vfE&T(f~(>@nEPtUHQbv~r)mA}t{E`Xk^cfOF;MbO3J z4AH&>x)i!hQ9O^<^P#ojilu!ybOrPR?cs&AUIe{3@suy&`%9rKbxvMJ>*dfZ5?8#^ zZ0E>T+Q+MCy&5tJ6j$^8HPCCdkJr(<26{adkJ20XCgbx)z2-Vv*F!fXdU+GyZ-j1w z-V9Ztw?J=oWA!%LZ-;J%-T@&q_XA9{ud*_&R8P;_e&x-UIbQ8=?0?eb6mX zjB*p-`k?`6GxR=a3pALx(^kH}A41abIRp(uDsT8Z0*yjQ9X`jQZIH?y{!T(NmbdBI zDOx+Ao$eWKr#%hrf_6iDpc!bdJAVi54??rhKIlWxeyA2oRoVxjgU})EVUE^e=m>OY z#If^yI|_YRuepoX0(8vv@)6n}g+6A!!{^7LPe7kcjKHV({%Pnl`uoq)`W*Cm_xmr< z{vz}xJ^N)^UxB^~#R$H}x35Fr&}+U)>s!#bL#;|%`u;BTJxJ#3`_SFcf4N?MK>LT# zkDwnzKY@M<{mh;JIqhFSztkRnMeEnlZxT=OTfQ$sb-kiNs|o!sv37pX_dh`Ytylb! z)}Nq1C+_hVzW){an_lsETK|Cl=`j3@_P?S3(X;pz1X*b&ZlpuHUGfI4~J4y0)Z z(zF9<>hv(}dqXEd_km7^?hD-yvJ$n!+@JOXpa((^f=-1V3_V0KbRbJR%)_9EgP{Xi z+F>3EJqj%0Y;+(^JIrIC$Lckw(Rv(|ad&t;?bD$r=-DUIdJ=R7^km0T9mvuSWNC+a z3iMQHC6t9$MN*;zN!o!V?ZA=|Bpy_NiqU9x7@zMYsH|56v?|ckptz@}^X)9?8G6k# zX*~-%J8_qD_`VuC7dj7G1Dy|9soH^5?Lew_nCC(lLKi_7Lzg7(aVg&~gPy0oJfGHD z=yFCalocIF)eiFl=!JUCi)g(VdWjpgm(soxdYR7b%W1s=dS#-ItN8vZ=+*jrL+fhj zHHi^>E#F@UU1O&Bd_D9A=vvpq8);t$U5{MtFgHMNf^KwYZ=x+T@@A+Cy#;zJ^tJ?o zxAXmG=pBmSowVKsy<6wtJ*r1LOgBf?LF=InP!H4#ZA{$fy?pP3ZqfT}qSX%#I3$~C zzYp4?X9sC*h2Ed|{Rj9y1P$vIx6&GcM&0knXpckN^y~z!Nocz}dmC*TogI31C#~C| z>BQZ4@qIV6N3WQnwHFdu8n1v4a%2|Tr`LRl)_$m#xXS^)AB04%c9=QnFm%MBxRdrg zbQB%hVLlAq1uZ0ge~j-Rfj+9g{}`>0L!U@o@kzda3i`A@&u3_T7W$m);q$b=0DTeq z67*&0E6`Wn`LEIbI`j>ly>HU`7WD0wr~D2_z6*U%F?^ra-H?^29p(qLe+d0Z&;FR! zPoST=`~Qsg&!J!F6aJFcub^MMv%jJJTWArgLk*}2{mz~LJ?%e0B1t>UAE7@%e|Bg8 zLi?}K-}K&pr}YoWO3@DUFWUcxM2dEp|AqcD*ohQ%RzL0KPzTfr-2=KObT8=MiKp*G zigqGJJMq{~;q$)G{U9qxJCUQE$k9$LQ#tz}=v3&z&_key>T2jjl6GQ~cA7`%Rga|g zDCp6VMDIk7c4CuuVv}|vMLV%cJ9(;3JUy|XJF!VS%@d&~L1#cuhPt2??)+0|KNVW3 zJ!EOEg3fejbF}l2r)LYaijeQlmS~ruK+jfaJq>z#Vx-RE`!k?tGBPLg_gT={khMoU zk(-^!%}(T|MZbo>=R;O@b|O1Fk)565NAh_gbP;rMq%%9QNIS7eJCUAZrF9}fW!#_d z5U!B}V8*zTX7BS+A(ldJ80yGt@zy=4~8# zJ9M*NBTxBG$XcSE=H0a419j`!b+pz)>do@E2kM13BAq+2LOYS1ok&j09h+$PL-@9Q zZie0mi3J+Us!lV=k*(1C^_maR8iI!19d4yP0*&g~FD`0v;ZB0J_3Cd`WWpNGDnXTM16 zOVF1S^ZOOPe--)~dbtyc*=fE3iM(|4``T?US;moXtzFjj?apP(s`nuPv{)NnJb0`z zx~uwNzBM)u)cgAy!#$b$?%C%0>Bh+D%_rw8rQE7qVU<^|-b=2T8fc7hHbPn!8V0yXJXUthwaeHPMjWInf-QuRelXEFPF^jMwDz z$Z&&St)H#W&T$(Vx3ae?7p%(pRY6xD=x%PEaO0NuOW8`X7*x-dGts!w=i=en4CB$< za*GU7!OwLyZr$3L8g7pE$Q_1y>XQqN?Sm^b9z(@vz5tT8L#8-o$X49@#tyo=k$GQs z<2~f#p7J5UZoIdAoWw`?c#Ta*fl+-Rhko8Sk)hwlw!ucvWMliRK2}~Is}k}+v6RgP zd9Uhoy?vI&xvh#IJLj<{=Ma;`BlD4$11n|*WjJ?jKrP!V*VXVI0*zL)jNg?#lXY3P`+4eJEm zVr{T7)zdgQA<#VEYX?aI#E=e6F)xk2vFc+5(&T9U_JR7r;l|{Arm?fXG0A$Et#2Qw ze_%SJ>qrOA^OPS1_xf2khJGavKjd=N(;}3Kp<6t(00-!z@q$!jA&v+c^Uxe0U=i9m zJ#@G^JkjVI)j3V@TaQeQK=T>3$||p5FF!onIJhrbf!nk|ok%7kaR_?2Co0GGR+OhiV0&uyf zz%BZ#az)_k^xDz4$o+h-0u%avwVZ;yVF$2rWG|A#E{=S$v@)}#hg6UuBn2tbZt>7m zeRke0jYzN1#J?NtUXu9Snw+U5qN#y}>YLr!HH6Fv2wKDy4ZgwY_W z4->+SN1P*~NoG|!CD?X!U44s4O6A-#5_XjrNuyFZTPT#$mq~cPB|V5N*TFk@OdhB< zQLpKQmP)H~ELq?2U%tR1%~ibWlVzZ}cKu97gtOIo!hn0{P;-5a(dkUy6OIcC?H+W# zMyE*_B4m3GR3FBznG6{vCWfiHb*?!uQ@?Fa1xUHLDjx$X2IX?L=#|R~$PP~iP%9VYSFBB=t7l& z0$QMF>tsm{k2#%CD&{ihtiA|s%EHRn``3>S)Q9$3K57SE zVMU|a9NSz!)B=0|JkzDJ)h>Sq33bMFHOEH9{29THm<^RS&r(7X+DR|^W8Cz5%Fmrqt?SLvQ1u6!h*gorL!!?dU)R^wBo*oSi>lxv`c(A)s zt6>(<&4kDrB-Fuu;WPMff(q&8s93?CTz~_>Z$c9Xf`mIF zYLHrGVcb%=%O2z5S7e;*KLL_9k7}PLIVX$~%CGt)L1Dd*+5v9MVl_@< ztJZgoB7v3DnIkm2TZn!EYps;?@-2g;mOFa3F}&cUS-I?GPQbwyNGr4hvhpN#kDSVg zX}PCI`u7=MY@tvQ1*Jl^?B&YUm1#qX*jzlc1Fcl=pYa&VyqZ90qe44v#Igz3lJbMv z5D`E09Khzj`Q*!=vnI3l#cMBLbFof3t_`4^X;uu1xojSXry4|v;uV#cWdz;ifSqL@ zX!Skn0r)FbKed}e1<*21qwAW(qgGz_A87O+IDgez0VT^E^QCB)xtGh8vY1|_>eC}o z>XJkYyn#SNjl*rI_KdYV8{>Nsz!)r3&5eh}IjD8F!=)&e;=(<7Jl%<(JFeE;(H`8B z#X~c=Zo*c(XYpELF&#tlf5{R+lMXXlHRX zw7V7n<9ip645$|efaZn?;XgI{k=Tew9U4{~UR(&quXx#FF_-R^9qne)xMNEn6Vr=# zPE;RhA5z4n==_l(5oSa@8wfKSmZ z!AF=S8iQf{uzeU1;$+AAwP>v>J0ZkcFu`#Ws~4p31aj7Svdv*E*g+?P@`ZxKmRHvY zFK|gm?U;LF8hkB?C%f@Ts;AI-V`v6eZfx5u2EERf+Dtj8Up+BYN}eAiq=B$bv?f%U z@tD2cqDxqQg`6(Na$%^V~~U>mVY%@{#lk0@0TqPIoK>j#GF+vE(G_rO}{?8KCos-FNF&okX7?faq^b9$6Z* z3pi=@J=37Z7GMeLm+iX*w74Uf^u-g*vqEeEJ>4zFl*B19pu|_Ukrw(!YYS>ZKO(_= zb zF5|UE4Qacdk;3Ruc4UhA&&hmNZD=%m<)Dx)RS@LaXpnWnkM}VOp($GQPRNBA**z_$ zJ>~??(UKq#^8q};=3^s+t!$TyUm|KDAMIA6-Yhu;BFabCsZf)U3$~Dz>dNLYnsOEn zO)PLr9_~7;F{qx|^3e4oHYzvYhbiM^St(y~vdlXnyIT}XaAB9~)Tj)qS^g23sUEKL z{7Tp~u#bEEQlSuJD}_R(`iux#DytNV8KMHPO4s5633f7`D>D+W+r41jnDprE(QbeO zOM!~h(wLjH-Zm@|8ehUgd$v{|EvK!7SD%LX1gpO}x|NV4E``n=yq}9q)M813vP@ng zD67mAsR?^K6>Y^_IIaOU_Y47M9-%F0Mbt97n5pA66(&|{E*>7lc3M1im_C%{by=dk z#Se0k1>9T-n@mUSALMf7Y$eDSs-zBqdlTZCJw7y%n=f^ zA%5*4Q$I8>-bOxXAC3{o)7>qQCub#wA&^B8EuaV`NP>@IH87Bgz_?U{dS7l8ONyqD zFO(AFnqdS*cC)bX;RItoj9O$$s^=27XWgo>T$+%9e-%=fi2JGG6%p8auROmvGV1pd z=EhdQVqhHSZgEn8jFLzJx709g$`T(fC4%(At*~Jj(;zrHHR}XHK3BF=o+g;0-dpex zti|M&WmMlhR6jU{;erir=Lkql4}+_`ivNHZaMl9RX7XEpE?BaR62KC{=1iVgQ9je0 z7_IluH4gN&)yaZU5ycqyV0aAjuxej{2uU-=JU9Doqyt|4~iEyMGJF-q9lGv0O)z(irHL&s7qR&h!It5LU{@HRHh6aSFd8zU8(aO z_8*TWN2Z1wu&uZ_82rt3bM@_eb#^5i7sZMz$eb*$YoHpJaYQ!BAWAda>ZFs_P_SR}%{n2;)9f=Gbp+vYO}K3Ob^PGY=`qZn|5k}Vh;!f>;*Nr-A}GvlDj zQUX>(1NH6Q5)u(7b^(2Zfh4E{h7!x#nf!r|txznI2(Cm>)9ig=Q#+bIkzQO(552ik zTaw32o!}5sJgj0l57!S4MLGu`cS&F^z>Yy3hJm#TR(}wbO4W-ac=1T4`6V_!xnrVI zMweSxCCZy%DDUpe6w0}BJIEox7RW>z_AjV$#H5L0-`U$7-Yc2oo@pl*%ZzAwRl!N0 zN~J`?p;W1)27H+$I7M>o{2`~VId+NM%<0#ENDYi99I35?sCgiHp`IpFoZ{LEGR2WZCJJhgO3$uGBaLO^8uZ$U(y z5>28(W~{ySH5(-gg$qmBt%#BrpbW|+XWB)d7UxFAQ3p%`CL>CRUxaFdr!rzlhPQ4r zSP~rV8(RvEIu22~Oe21W4B5~S#RASfk}M5n9Bu*w-_K|LpqyXgrN zKjZFdQ!?VjodDhFuBGVq^t|LA#F?B|skVQRM(Y9 zVrwS=wI_~g$owK(h-rwPkRc=IJ+4lg)DXZ>ZYm3T;u+{ZtaL$yi#Nt|or4#ugF zTUpM@Z01YqL3W9$&6&+ZjSYvXc98)c9`hQtnKBvx{gi5IN9bAx5gpJsRb|iCGS##V zLU4E)nuHEn5Zq4cA)|BIa*=w26|DmtN8NjmF4mlBkSStT$;xOqdfImq4N6i3poBY} zX2T;Ko|8375CFEDm=eF2LJ*rMfd8_Mz(Q+ zW$6T2uSBB_XbW*EXkxUFlbIyshE~Ld+CJ(mbpD9r+0~frS)sZanTI9`?1DeMPi!Bn z0=53BR4!2jRZPzaF@w^5BKRstV6*M2Kz$S_cXx}yE8zX-y-Fz&d2|+tGl5y~^<5e* zD-w%BC2g9QwD9UP&&!nBFGSm4%K(h76L(GWAI;4R^wAtTM(K>?nvV@hc$(@jM8AYf zN%2G;99U5)&Y}{I4&xSufYN}#BQ4n)w@A~fQMAufhq>LPDZ(X~xt(e#(U+2Qmv~D3 zpw>hY@I0(TT4Fq5e;Zrj7zU432?ZZzRq)G!W!3?Soyq7rzFM5)K(`q6a8w?9D_uWH zD1CUY1s9PuQjSswCi26&Iz-cGY}De4Uag-Kae|6+Q;k|LWC4_`o)xbg0fVWC%9TH;DFQ*&NRom$*j8+S6MTWp$;Wq zU&@j|$tO}w%8qJ;y3~D3va47wv`uyiCPlCm(`0ufdp8|*xvs#P!=aMEKDUwowEC@t zXwb}(s!*Red8yqL0%J1vs|DE3Uesgt;W8CVqKf#5tcNJTjRWQ<9n>&%B+7|MV^?!v zK-OeWUlem(;aqU!8>T=dpQwO1K9YoGtlSqyGnntNXvR(X3G}Ufik9Ieud{7X9X(i+ z6+Kg5FHVODI)(`$s!;$!a~efe?IAYQszT0s0y5L_D)&oXG3yt|dB))xT|rTFvLz~$ zFBUUM83|Eac9m+yRIig6P^2whr-&Ju8+^C1QI)+sLv!nVeb-#Q-)3ZqcVN!M#wfWF zY>8^FWl6=Rghb@$wyHaPaIP`2L!wjL2Ja+@Ak^#^9o&kj82?Cyk2rQ-; z<)LmdHnCZy)&#$^acr+Vv}EFKSrw)OUsMs>QFN9~xm@y@?@CTZZ**~{pk;m~%;>gG zde~=-;wXW-X>l{Ga+549HcL1{OkK!iKCx<#EL>t4t4vIW{X_yu^$p>SLv}%vMU_C7 zBuV}svpED3K@;`FD(Kl47&Pm6(0vd3SNJxn&lh1S@eZ2FRXGnjvL~c4 z8pNT)7O*CY1>#w8PEHV~SPz1lw_0TiztqKrZdZ8Simd{EM%h`7| z-2iAaz7AUwE%l*|{>hLNT;VfD+sG_YYecm&tb{JHHJskjka`}gC$3#nPO#Bmh8M#J zKxl3!9#71mFogvX!`e6~gGgnmCNPG3Bv1sVi7`PFpig9?oedUX;g<85|CdDr?#%5B ztOEZPba28X539x+8`yc6D;ji>dG4t(&ypa{V5vs^rRn=dh%SM5he({s!?7vk$}C5w zL~h$^F=_(PEpgc~u_G@}ZMM|52qTTp%P7Wy1rhp3M~jRE(ZO6+9sp(or6-5@1K4zY z4o!@&pE&hsG@eK2Bv3eo$v2|2i-|_Lf*Vg34R$w@P&IPPTB-N5__kw(;!DqGN|G(q zmY?;@8H#_SyQZQFF~q1aI#HKoe)%4nyeV z+bfi^kVaclIc%k7Z0cg*c8QxW{;Vbt@J#w^QM!GFi;bj04gn@1i-;pCeO$V@LF(!D z1?5jHP2wS8z@`L$67SDfUl8$UteNAeh}cWyh}B%nyIN1MtyC|pMI-JPdj+QoK0T2z zC3L4yTfpOh&D~=2*z~072`Rtpu{l&xN>mEi{1hA+`C>kR4 zWHJO=YA%J<&0BXd11hERl`eaLC7#C-%18w(OC(*M-|8U6AV;L+0>-d{lj;lV1;%e9 z*DH)!wi+jNfl4TJKPZ%!it)%_lrYxt7>^vr$i#;L6NUU~Ll*X(K!Z2kEg3r%<5Gu7 zW;~o!N#jub7^cOsvhfTlO*=bfnY3y=o{(J_Hr{VHVjzyZLKil(oM@;0MjVuxRRI zF#6>vWfk~&P!lZDog^+4xkxIOazoKc+6uw?6{kz{6ijr%ob~l{Z3jKxW()R9k*qDit_VZ^{Drld@+W`VIF zQLjmpIGlg9IMp0Zx>NH_EEI!CW4Mk@trq7FVxP&Zg%!vWTJ++`3$k66cwCXp?wrlk zO^N~(c9=-tv`7HdB8{<6&WWs}NJips)E6BBm(^&(+nhXTB$pHL`}PtI-=Jq0y3U$0BQFvfDR5JsN)3xn3Qmk zNt6l3REAkYQ<WQBBU{6ArVeT+R0hh!0E$V&xSnqL!ForV-w^9WaHF1jcjG z$MKeu6jJT9`+Eo#u$G)c@)4Waykt}VpCY=vCz+6T)-MmQ9WRAK*{*n6;zS;z#6>In zd}2K~jIk?GCRk49C@qN6!t6}D-3ge_h3gr+DSgTW&YcrW;K-Co)uzq2SZ`B3l4kE_ zojI{2B9{x%Oo0&LW-Zc{Ob9#!54V(N;OfXG5PXBiwqChl<7UE9HW#E*>2X5c+OVVc zfPvQn7{;XBK+2wlx%6A6xqVOq{HUBLm&?{2Ef;c*B0Czgn1L0$E5f>62*-rM*fU>0 zq(vEoe%i$$qaIrkQ4_dJMhamd>7TM18BRy?@L|n+fm9^ho0!zrC;;Om;6-|gjOX3Ui%|fAJOo)w2})kD`od-( zQQAQw7F59NFcSd4VDRkKOg&{OgtIhXRV;W+G@l6{JVxH)L)wsv@UFGrsMqLfjLTO2 z6(k~h#DyeRbnAjE+Sd4Pn`NbNTXYo9)=t-v2S}#WW%#M$oSk=CS3)J2g0b=_vGgS= zVBr=5$py;Zfmokuv?YqW+3hAALu;!OL4iQJ>lOCgGH8U!u^U960krFaOIC<$d2}C( zlz~WWUPKzX0unZeqR+&0h~;D$Rk3B{C=k*AB&}UZ9edxiR9gk@gBD;@BuQ*iB#)d7 zOAq>|q=bqKb-#s5Ni_t*z!army#AoVPVTEx7SNKU4!clgCzA|V1hUe_BiC=cMY&PJ z;u*ozKPl;1u|`?}v=IdsbjxGl-{Dq>_NnjH2bk)1l1l;wVOqT)Hh7M`@^P(`Omyq= z(WOH0NA_PJk?;<5_ki6L-7a~@n2C>>sOyj;6jO$q2$6YYRczrak#W8l}TT^w(FK5(puWkwZ>~i)`^-Soung$J%zy+Lh&nv`~pOJ z+K$!4JSXz{Wb|!Tru51afJ&6}x)GWhRs#k}lFn6T|fXv)@b3)=`7s_C$%L^{I}aHtg+p@?I3O4!LM-Xb-HWsiE| zGoyvjDlNHjyea(^6;c23oKdrh27rM?!-1kXF;|}v-=%Ta98L_op%N_a zEIc-pQW;rBmPs5*gv2=ER*X$_0UK*X9TSa~68BjVXQMOhGj3N5Cq<(QAPGsrDy|rg zAfLzy-FjkEFK(!)Fq9HLXY)xN9Q#KJA72^`Rf4TuNLwvdm94A+G8x2>*mh!_bXj!x zZE4-5PA(;{in0aw#I(5RBIvAwv}+OD2q#s?kTRJFwY=iPlW+GLOEpFWO1fG z1q*3u1wg6n_C{v{WGHQ-il`%q&&zapxOPvWm|LC#jYv`{+-E90+CQb}F|!%< zFfxXTExQm4siB?}aieP9$Znl;WS zMm6CdtM%MId9({2ii=PM0d@n{roS{#mtsg3bCrzL$PEt=fRn{TMoHt&Y)SLmIW8fz z#E|Yn#8c@LYCwrNjuX44cJ_nKEjHM!7;_FIdk8AnHoO!ecGq>`N9pwVoiAQlyD;s8j(lAoE@(R)Sy(Eg7*A(I9rKwoDKK7;y)v ze5xJO)lZhpR?7sc--dM?FDdF`RPiQpd`%`21>Zg@yQxpO9VS8=4=Q4hq7F|EcRPMBnl}A|`rOHLxsTH)81+E-un1u?T$JaPj#*+rh-ih|8)H@-CJX za14Q8tfOMig#c1l8hEt#<5wD`P+F#@3IYkwYzyb!HVbns-Z@{tU3Ui)la>K?=tzUJ zTZcUeOJ4ldA{5wBP~R=(j>uezs1*ts*&fCATa*v6v+hy$;bzW(O~BqZNfyFO4x~uk z4fZ`eB41d*Xpo^9=aNgX03##}SQ9$OT+7*gbx05RNc?dzQEipK$Qm+8cB_OW!5Pd- zk(^(fiM72a@yKqDBYVS%)iH!o=6G(Eszi3A*e}eAWa9ULQDJ542je|v@g&C|*t0xL$&q^RBui%OUf_8G5bY?SCe2_I1fCa4;idoM23`)xoHp2fYj zZ}efqT8j}=V6z9-b28Sf`oa2?wrt$_7Uf*HDOhi9O|foeGV9%H^4S235%0}y(Z$z)@6kG$db15NsT}XuvYT^CV6{? zh{n4=JaH|aeudYpKfrOJkBD;x6R78c*{vXVo#p^Ws3cqYn& zaj<>YigfLZlO!Nhv@dqRf>@d&OVCb?PV1H+Ey@h_Zn4O|UhEh@@rIxndmOKoNa&Ol z%Zn8DE0r!e#W&_ck_}jQCrVe#s}%)PJjgrX;iIB9M39PI#EU`?PV?eSXOvO+Ke@{W zv7-PJ#eMs%@(*=SC$>Cb@3ebFgF=Z6g6F9&KV-7>2G$KW&#nq-5!xxbjw8 zcOw>jWT92Ccx3x}x)uw2NcKs&sCWwM+Y-G(Dr^Aka1EdSylrIgAU$tabeak#h+!$ z^d+k4ia*6YiPLjhJ?bPmube8`_daIq9aG_iowvZ**(;d=sUQN58jMt_+py-4Zrzl( z#}J}$MlqwE$J`*MdjUULs<7I#b|rH0OPTsDbJi2()oT(x6v>cghJaVm#dr@(bS8{E ziBUq4lD@tq-W)*)3^gbVp~-`6XOShhNRU7#f85?orXWf7HBZ?+~CQPr-9`4WaMvbi~I zxB3(Fkoq9W8QV9yE|p23(Y}vYR-BpcmhwqaR`pGz_1X}j4opT~At;dS3%Eid4_}M< z&AvI=zZ}OAWT!d18;AGeYpJxLJW7)ynWCU#V@x_j*}hxGXitxn$w@hy8-9@k${hkn z%xhTQ>6{{4GPAs6w@vIV;wd1B}A7s_+0_QTDj>;d<)IoUPv(&$mepRn&Ll9p9Jo)`KA9FVv% zu`;y2zUZ}H+bn8RcY|u_tZr9fw6$=R`qp;wt_XDEF+@%`4o+#lSY)G=yiZY!-12@c z8yiiO3R$1RB?nkXyrKkSBoP9O-d=jgU19lr_^O*yS<4%wL=;;_^=S&1P&Z|f#aKuB z#LY*U-b+7breD|@UdrPaH2<=-vQ*Hjj0 zhP}slcr}Q;*2L~+yXe_sa=`z0&4|dD>RJ0jEi_OT(DfZf3@aX0fPQIdj=q%*h?7dC z`1eR;%<;twRPk}y^s z?xWA>MtYu=k_L4M^fd}yQcud(Sj~ys;5MNl`*vas_}zW!gBl`EJ_8#a!;C^WUHaNe z_LNha>^7jPc5y0D`lYClcN&Djc}ymP8#yGqz8q3Ll$Z$;#`VT2c`FemVyTh}+uMR^ zfrs&o~1qoqxzA#n3>OyoOJ@FY&S@VTo`m ztUVvD9Dyo{_^_-_3XQ_;G3{ntko3jHIbfGxjUNa@?kG?RFR69rxHh2L1nbQGZwm;? zhZBue%o_sAWNG3TYY5te?`M;3y4|T9y+SWF2^b+sj-tp+GAtS&g8h)wWEZ#3A}V#N zT_w!T_m}^Pg70H_QJENIlWF?cyO7deS1?d;;@zVd!yRr@L@THiLklX&B#$W*3M6*D zqQk7ILdA^A98CPU))z~g_xR?6rBU9NS#2?O;%rsk_vIy;GA9}aAoS~cOw;ZHSHUfX zyR%m>7n5uZ%R}nrGU89|V!uYB&@f;`nQEt62%%A5An&eR8wsey;)!xB+(@isSv@(4TEfxGK0^}-Q*-7S}7kb)~ofAC5&R%(hOI9qu`(Br=x%9G&*6dw&qu3|<@z1B9 z`?M>sxasN}Z@Ti_%g$MS=_Sv;^1Q2Wdc&Kqzv0TY*Iaw;Rp(rM*(<7?zH;>|-gwnD z*IjwT)z{x}?bX*@_nPZh-SGCiPde+3*PH6KS3k3Z``r{QU$*RF%TLvR?ceb8Vawy= z(n@|0zr|O`9qbjE<(cKD^Y0n%^R(siuN;woGs~Z-pQq6}gSI^@N2Pr_WY61s$aniI zxkAp#S$jqJ3%SOg3GX1kk!$R4rKh+bi{%W?2|mGUk66_9PVx)6gM5=ahkxZ-`HkMc Id)Y7l4@@ctLjV8( literal 0 HcmV?d00001 diff --git a/B_main/__pycache__/phonelist.cpython-38.pyc b/B_main/__pycache__/phonelist.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c13f5defcf38da41579ef1aadbfda5c4c4eced3 GIT binary patch literal 1033 zcmZuw&r2IY6rSCzX^h5SEtHm0_PU2H#w7k~DfJia!Kxrqkie2UOAU!eb~hA@2a9`1 ziw6&iiEJdfw4_uTtpyAAAIY`1?CiCNp8DR7k(73pnSI}U^WJ>l%y)Wz-SYSsr+5J>=t7Ue$C$r;%`+lE&1gdnv$`^{z=$V+EoqsVitt~v8pPN}!#D=t< zTGJaH8>_i>y+#ZPw)j0hHJ)5Db;C?fElf<#&pk~}>t@EvZ6p`d*=%a!`GS@QIytGW zrPAw3KR25;tp^*MjlQ(0rLx)FYn^I7(Tk>hx1te*cm?`j4ZcI}yc+_GSVBdtk{!W! zd>^K`FL%$8x%VrFvRf~DrJ@pONVvBlz|w}4(Kk)sMBC7TO#dt0x)z3>&6_aY5{6&K z3RY+}jIV{^KVy-Kutlm|mJAbI40A5sDky8a3z=2x-qEfia!CQ$SP=|7f&Xgq4@rBu zsiCObu&SjA|HU~)@~ikKXP`<0r`rnQg&DrX(4(~+&1dz;sN1{?gDCv-{0c@wQb_s( D@|!4! literal 0 HcmV?d00001 diff --git a/B_main/__pycache__/urls.cpython-38.pyc b/B_main/__pycache__/urls.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78d1067841a979bdd6f7d1e068b3993787451cef GIT binary patch literal 839 zcmZ9K&2HL25XaXT1I8F|KANVLdgoA34^^coiV7i$Dm5WW%EdymEM@~Hw%6UYgW%E^ zXda@+zDo~1@3p5qL8YELi%r#bSN`k&oAIn>KgPCQCpbQR`8>M4GE<1WKTXrAT*m^aRRbIWk;h?^ zqaYi}f@VQ2qz1GLa*%A;DQFj|33~hu*PB`H>2o^kj1r@_;SlkomB#3hMLeyeIA4cnM`mNY`fK3$bKaEuF3PIQ^If zoMKhTW-Lp4vXYy%6|G(-oZSW??a6wy@(Ng+%OITt_m@3c+uC(YP$2>qd)sB2{|2zD zU>0WyuAdv+kqvB`MKn%5&RBZc*_Re9{CLK?84m?7;qmaQk}}Gl+?2()+`x!?Q2ebT zo04vRXuP6|YD&~=;Ug5r92kW7@iHr=hD15 z8@}s%B+!TmpW0~>?SK&JB}D+Aw5wqLcBm}Xq%}wwg2YNa-*XC1}9VRdjJ3c literal 0 HcmV?d00001 diff --git a/B_main/__pycache__/views.cpython-38.pyc b/B_main/__pycache__/views.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbe18d9579585e385a29dfc5c8935ed8e953b603 GIT binary patch literal 8552 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', + obj.사진.url + ) + return "사진 없음" + 사진미리보기.short_description = '사진 미리보기' + + def 모든사람보기권한(self, obj): + if obj.모든사람보기권한: + return format_html('✓ 모든 사람 보기') + else: + return format_html('👤 회원가입자만 보기') + 모든사람보기권한.short_description = '보기 권한' + + def 비밀번호설정필요(self, obj): + if obj.비밀번호설정필요: + return format_html('⚠️ 비밀번호 설정 필요') + else: + return format_html('✓ 비밀번호 설정 완료') + 비밀번호설정필요.short_description = '비밀번호 설정 상태' + + def 수정일시(self, obj): + return obj.user.date_joined if obj.user else 'N/A' + 수정일시.short_description = '수정일시' + + def has_delete_permission(self, request, obj=None): + return request.user.is_superuser + + def has_add_permission(self, request): + return request.user.is_superuser + + def has_change_permission(self, request, obj=None): + return request.user.is_superuser + + def has_view_permission(self, request, obj=None): + return request.user.is_superuser \ No newline at end of file diff --git a/B_main/apps.py b/B_main/apps.py new file mode 100644 index 0000000..18edb53 --- /dev/null +++ b/B_main/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class BMainConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'B_main' diff --git a/B_main/clean_duplicates.py b/B_main/clean_duplicates.py new file mode 100644 index 0000000..7943d2d --- /dev/null +++ b/B_main/clean_duplicates.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +""" +중복된 Person 데이터를 정리하는 스크립트 +""" + +import os +import sys +import django + +# Django 설정 +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'A_core.settings') +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +django.setup() + +from B_main.models import Person +from django.contrib.auth.models import User + +def clean_duplicates(): + """중복된 Person 데이터 정리""" + print("=" * 60) + print("중복 Person 데이터 정리") + print("=" * 60) + + # 전화번호별로 그룹화 + phone_groups = {} + for person in Person.objects.all(): + if person.연락처: + if person.연락처 not in phone_groups: + phone_groups[person.연락처] = [] + phone_groups[person.연락처].append(person) + + deleted_count = 0 + + for phone, persons in phone_groups.items(): + if len(persons) > 1: + print(f"\n전화번호 {phone}에 대한 중복 발견:") + + # 회원가입 상태별로 분류 + registered = [p for p in persons if p.회원가입상태 == '회원가입'] + not_registered = [p for p in persons if p.회원가입상태 == '미가입'] + withdrawn = [p for p in persons if p.회원가입상태 == '탈퇴'] + + print(f" 회원가입: {len(registered)}개") + print(f" 미가입: {len(not_registered)}개") + print(f" 탈퇴: {len(withdrawn)}개") + + # 정리 로직 + if registered: + # 회원가입된 것이 있으면 나머지 삭제 + keep_person = registered[0] + to_delete = persons[1:] # 첫 번째 것 제외하고 모두 삭제 + + print(f" 유지: {keep_person.이름} (ID: {keep_person.id}, 회원가입상태: {keep_person.회원가입상태})") + + for person in to_delete: + print(f" 삭제: {person.이름} (ID: {person.id}, 회원가입상태: {person.회원가입상태})") + person.delete() + deleted_count += 1 + + elif not_registered: + # 미가입만 있으면 첫 번째 것만 유지 + keep_person = not_registered[0] + to_delete = not_registered[1:] + withdrawn + + print(f" 유지: {keep_person.이름} (ID: {keep_person.id}, 회원가입상태: {keep_person.회원가입상태})") + + for person in to_delete: + print(f" 삭제: {person.이름} (ID: {person.id}, 회원가입상태: {person.회원가입상태})") + person.delete() + deleted_count += 1 + + elif withdrawn: + # 탈퇴만 있으면 첫 번째 것만 유지 + keep_person = withdrawn[0] + to_delete = withdrawn[1:] + + print(f" 유지: {keep_person.이름} (ID: {keep_person.id}, 회원가입상태: {keep_person.회원가입상태})") + + for person in to_delete: + print(f" 삭제: {person.이름} (ID: {person.id}, 회원가입상태: {person.회원가입상태})") + person.delete() + deleted_count += 1 + + print(f"\n총 {deleted_count}개의 중복 데이터가 삭제되었습니다.") + + # 최종 확인 + print("\n최종 중복 확인:") + final_phone_counts = {} + for person in Person.objects.all(): + if person.연락처: + final_phone_counts[person.연락처] = final_phone_counts.get(person.연락처, 0) + 1 + + final_duplicates = {phone: count for phone, count in final_phone_counts.items() if count > 1} + if final_duplicates: + print("여전히 중복된 전화번호가 있습니다:") + for phone, count in final_duplicates.items(): + print(f" {phone}: {count}개") + else: + print("모든 중복이 해결되었습니다.") + +if __name__ == '__main__': + clean_duplicates() \ No newline at end of file diff --git a/B_main/forms.py b/B_main/forms.py new file mode 100644 index 0000000..61fa600 --- /dev/null +++ b/B_main/forms.py @@ -0,0 +1,297 @@ +import re +from django import forms +from django.contrib.auth.models import User +from .models import Person + +def format_phone_number(phone): + """전화번호에서 대시 제거""" + return re.sub(r'[^0-9]', '', phone) + +def format_phone_with_dash(phone): + """전화번호에 대시 추가 (010-1234-5678 형식)""" + phone = format_phone_number(phone) + if len(phone) == 11: + return f"{phone[:3]}-{phone[3:7]}-{phone[7:]}" + return phone + +# 허가된 사람들의 정보 (실제 데이터로 교체 필요) +PEOPLE = [ + {'이름': '김봉수', '연락처': '01033433319'}, + # ... 더 많은 사람들 +] + +def is_allowed_person(name, phone): + """허가된 사람인지 확인""" + formatted_phone = format_phone_number(phone) + for person in PEOPLE: + if person['이름'] == name and person['연락처'] == formatted_phone: + return True + return False + +def is_already_registered(name, phone): + """이미 가입한 사용자인지 확인""" + # 전화번호 포맷팅 적용 + formatted_phone = format_phone_number(phone) + + # 전화번호로 User 검색 (username이 전화번호이므로) + existing_user = User.objects.filter(username=formatted_phone).first() + if existing_user: + # 해당 User와 연결된 Person이 있는 경우 (이미 회원가입한 상태) + try: + person = Person.objects.get(user=existing_user) + return True + except Person.DoesNotExist: + pass + + # 이름과 전화번호로 Person 검색 (user가 있는 경우만 - 이미 회원가입한 상태) + if Person.objects.filter( + 이름=name, + 연락처=formatted_phone, + user__isnull=False + ).exists(): + return True + + return False + +def get_allowed_names(): + """허가된 모든 이름 목록 반환""" + return [person['이름'] for person in PEOPLE] + +def get_phone_by_name(name): + """이름으로 전화번호 찾기""" + for person in PEOPLE: + if person['이름'] == name: + return person['연락처'] + return None + +class CustomFileInput(forms.FileInput): + """"Currently:" 텍스트를 제거하는 커스텀 파일 입력 위젯""" + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + # "Currently:" 텍스트 제거 + if 'help_text' in context: + context['help_text'] = '' + return context + +class Step1PhoneForm(forms.Form): + name = forms.CharField( + max_length=50, + label='이름', + widget=forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '이름' + }) + ) + phone = forms.CharField( + max_length=11, + label='전화번호', + widget=forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '01012345678' + }) + ) + verification_code = forms.CharField( + max_length=6, + label='인증번호', + required=False, + widget=forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '6자리 인증번호' + }) + ) + + def clean(self): + cleaned_data = super().clean() + name = cleaned_data.get('name') + phone = cleaned_data.get('phone') + + if name and phone: + # 전화번호 포맷팅 적용 (대시 제거) + formatted_phone = format_phone_number(phone) + cleaned_data['phone'] = formatted_phone + + # 이미 가입한 사용자인지 먼저 확인 + if is_already_registered(name, formatted_phone): + raise forms.ValidationError('이미 가입한 유저입니다') + + # 허가되지 않은 사용자인지 확인 + if not is_allowed_person(name, formatted_phone): + raise forms.ValidationError('초대되지 않은 사용자입니다') + + return cleaned_data + +class Step2AccountForm(forms.Form): + password1 = forms.CharField( + label='Password', + widget=forms.PasswordInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': 'Password' + }) + ) + password2 = forms.CharField( + label='Password (again)', + widget=forms.PasswordInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': 'Password (again)' + }) + ) + privacy_agreement = forms.BooleanField( + required=True, + label='정보공개 동의', + widget=forms.CheckboxInput(attrs={ + 'class': 'w-4 h-4 text-blue-600 bg-gray-700 border-gray-600 rounded focus:ring-blue-500 focus:ring-2' + }) + ) + + def clean(self): + cleaned_data = super().clean() + password1 = cleaned_data.get('password1') + password2 = cleaned_data.get('password2') + + if password1 and password2 and password1 != password2: + raise forms.ValidationError('비밀번호가 일치하지 않습니다.') + + return cleaned_data + + def save(self, name, phone, request, commit=True): + # 전화번호 포맷팅 적용 + formatted_phone = format_phone_number(phone) + + # 기존 사용자가 있는지 확인 (전화번호로) + existing_user = User.objects.filter(username=formatted_phone).first() + if existing_user: + # 해당 User와 연결된 Person이 있는 경우 (이미 회원가입한 상태) + try: + person = Person.objects.get(user=existing_user) + print(f"[DEBUG] 기존 회원가입 사용자 발견: {existing_user.username}") + return existing_user + except Person.DoesNotExist: + pass + + try: + # 새 사용자 생성 (전화번호를 username으로 사용) + user = User.objects.create_user( + username=formatted_phone, + email='', # 이메일은 빈 값으로 설정 + password=self.cleaned_data['password1'], + first_name=name + ) + + # 기존 Person 정보가 있는지 확인 (user가 없는 상태) + # 전화번호는 대시 제거하여 비교 + existing_person = Person.objects.filter( + 이름=name, + user__isnull=True + ).first() + + # 전화번호 비교 (대시 제거하여) + if existing_person: + person_phone_clean = re.sub(r'[^0-9]', '', existing_person.연락처) + if person_phone_clean != formatted_phone: + existing_person = None + + if existing_person: + # 기존 미가입 Person이 있으면 user 연결 + existing_person.user = user + existing_person.save() + print(f"[DEBUG] 기존 Person 업데이트: {name} (user 연결)") + return user + else: + # 기존 Person이 없으면 새로 생성 + Person.objects.create( + user=user, + 이름=name, + 연락처=format_phone_with_dash(formatted_phone), # 대시 있는 전화번호로 저장 + 소속='', + 직책='', + 주소='', + 사진='profile_photos/default_user.png' + ) + print(f"[DEBUG] 새 Person 생성: {name}") + return user + except Exception as e: + print(f"[DEBUG] 사용자 생성 중 오류: {e}") + # 이미 존재하는 사용자인 경우 기존 사용자 반환 + existing_user = User.objects.filter(username=formatted_phone).first() + if existing_user: + return existing_user + raise e + +class PhoneVerificationForm(forms.Form): + """전화번호 인증 폼""" + name = forms.ChoiceField( + choices=[('', '이름을 선택하세요')] + [(name, name) for name in get_allowed_names()], + label='이름', + widget=forms.Select(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition' + }) + ) + phone = forms.CharField( + max_length=11, + label='전화번호', + widget=forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '01012345678' + }) + ) + verification_code = forms.CharField( + max_length=6, + label='인증번호', + widget=forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '6자리 인증번호' + }) + ) + + def clean(self): + cleaned_data = super().clean() + name = cleaned_data.get('name') + phone = cleaned_data.get('phone') + + if name and phone: + # 전화번호 포맷팅 적용 (대시 제거) + formatted_phone = format_phone_number(phone) + cleaned_data['phone'] = formatted_phone + + if not is_allowed_person(name, formatted_phone): + raise forms.ValidationError('이름과 전화번호가 일치하지 않습니다.') + + return cleaned_data + +class PersonForm(forms.ModelForm): + """Person 모델 폼""" + class Meta: + model = Person + fields = ['이름', '소속', '직책', '연락처', '주소', '생년월일', '사진', 'keyword1'] + widgets = { + '이름': forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-200 bg-opacity-80 text-gray-500 border border-gray-300 focus:outline-none', + 'readonly': True, + 'tabindex': '-1', + }), + '소속': forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition' + }), + '직책': forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition' + }), + '연락처': forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-200 bg-opacity-80 text-gray-500 border border-gray-300 focus:outline-none', + 'readonly': True, + 'tabindex': '-1', + }), + '주소': forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition' + }), + '생년월일': forms.DateInput(attrs={ + 'type': 'date', + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition' + }), + '사진': CustomFileInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition' + }), + 'keyword1': forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '검색 키워드 (예: 회계감사)' + }), + } diff --git a/B_main/manual_populate.py b/B_main/manual_populate.py new file mode 100644 index 0000000..b0aecf7 --- /dev/null +++ b/B_main/manual_populate.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python +""" +수동으로 Person 데이터를 초기화하고 peopleinfo.py의 데이터로 채우는 스크립트 + +사용법: +python manage.py shell +exec(open('B_main/manual_populate.py').read()) +""" + +import os +import sys +import django +from datetime import datetime + +# Django 설정 +import sys +import os + +# 프로젝트 루트 경로를 Python 경로에 추가 +project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, project_root) + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'A_core.settings') +django.setup() + +from django.contrib.auth.models import User +from B_main.models import Person +from B_main.peopleinfo import PEOPLE + +def clear_existing_persons(): + """superuser를 제외한 모든 Person을 삭제""" + print("기존 Person 데이터 정리 중...") + + # superuser들 찾기 + superusers = User.objects.filter(is_superuser=True) + + if superusers.exists(): + print(f"Superuser 발견: {superusers.count()}명") + + # 모든 superuser의 Person들을 보존 + preserved_persons = [] + for superuser in superusers: + person = Person.objects.filter(user=superuser).first() + if person: + preserved_persons.append(person) + print(f"보존할 Person: {person.이름} (ID: {superuser.id}, 이메일: {superuser.email})") + else: + print(f"Superuser {superuser.username} (ID: {superuser.id}, 이메일: {superuser.email}) - Person 없음") + + # 모든 Person 삭제 (superuser들 제외) + deleted_count = Person.objects.exclude(user__in=superusers).delete()[0] + print(f"삭제된 Person 수: {deleted_count}") + + # 보존된 Person 중 첫 번째를 반환 (또는 None) + return preserved_persons[0] if preserved_persons else None + + else: + print("Superuser를 찾을 수 없습니다.") + # 모든 Person 삭제 + deleted_count = Person.objects.all().delete()[0] + print(f"삭제된 Person 수: {deleted_count}") + return None + +def parse_birth_date(birth_str): + """생년월일 문자열을 Date 객체로 변환""" + if not birth_str or birth_str == '': + return None + + try: + # "1960.03.27" 형식을 파싱 + if '.' in birth_str: + return datetime.strptime(birth_str, '%Y.%m.%d').date() + # "1962" 형식도 처리 + elif len(birth_str) == 4: + return datetime.strptime(f"{birth_str}.01.01", '%Y.%m.%d').date() + else: + return None + except ValueError: + print(f"생년월일 파싱 오류: {birth_str}") + return None + +def create_persons_from_peopleinfo(): + """peopleinfo.py의 데이터로 Person 객체 생성""" + print("peopleinfo.py 데이터로 Person 생성 중...") + + created_count = 0 + error_count = 0 + + for person_data in PEOPLE: + try: + # 기본 필드들 + name = person_data.get('이름', '') + affiliation = person_data.get('소속', '') + birth_date = parse_birth_date(person_data.get('생년월일', '')) + position = person_data.get('직책', '') + phone = person_data.get('연락처', '') + address = person_data.get('주소', '') + # 사진 경로에서 'media/' 접두사 제거 + photo = person_data.get('사진', 'profile_photos/default_user.png') + if photo.startswith('media/'): + photo = photo[6:] # 'media/' 제거 + title = person_data.get('TITLE', '') + sequence = person_data.get('SEQUENCE', None) + + # SEQUENCE를 정수로 변환 + if sequence and sequence != '': + try: + sequence = int(sequence) + except ValueError: + sequence = None + else: + sequence = None + + # 이미 존재하는지 확인 + existing_person = Person.objects.filter(이름=name, 연락처=phone).first() + + if existing_person: + print(f"이미 존재하는 Person: {name} ({phone})") + continue + + # 김봉수, 김태형만 보이게 설정, 나머지는 안보이게 설정 + show_in_main = name in ['김봉수', '김태형'] + + # 새 Person 생성 + person = Person.objects.create( + 이름=name, + 소속=affiliation, + 생년월일=birth_date, + 직책=position, + 연락처=phone, + 주소=address, + 사진=photo, + TITLE=title, + SEQUENCE=sequence, + 보일지여부=show_in_main + ) + + created_count += 1 + print(f"생성됨: {name} ({phone})") + + except Exception as e: + error_count += 1 + print(f"오류 발생 ({name}): {str(e)}") + continue + + print(f"\n생성 완료: {created_count}개") + print(f"오류 발생: {error_count}개") + + return created_count + +def main(): + """메인 실행 함수""" + print("=" * 50) + print("Person 데이터 초기화 및 재생성") + print("=" * 50) + + # 1. 기존 데이터 정리 + preserved_person = clear_existing_persons() + + # 2. peopleinfo.py 데이터로 새로 생성 + created_count = create_persons_from_peopleinfo() + + # 3. 결과 요약 + total_persons = Person.objects.count() + print("\n" + "=" * 50) + print("작업 완료!") + print(f"총 Person 수: {total_persons}") + if preserved_person: + print(f"보존된 Person: {preserved_person.이름} (Superuser)") + print("=" * 50) + +if __name__ == "__main__": + # 직접 실행 + main() \ No newline at end of file diff --git a/B_main/migrations/0001_initial.py b/B_main/migrations/0001_initial.py new file mode 100644 index 0000000..00d73dd --- /dev/null +++ b/B_main/migrations/0001_initial.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.16 on 2025-07-31 10:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Person', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('이름', models.CharField(max_length=50)), + ('소속', models.CharField(max_length=100)), + ('생년월일', models.DateField(blank=True, null=True)), + ('직책', models.CharField(max_length=50)), + ('연락처', models.CharField(max_length=20)), + ('주소', models.CharField(max_length=255)), + ('사진', models.CharField(max_length=255)), + ('TITLE', models.CharField(blank=True, max_length=50, null=True)), + ('SEQUENCE', models.IntegerField(blank=True, null=True)), + ], + ), + ] diff --git a/B_main/migrations/0002_person_user.py b/B_main/migrations/0002_person_user.py new file mode 100644 index 0000000..68ba0d9 --- /dev/null +++ b/B_main/migrations/0002_person_user.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.16 on 2025-07-31 10:26 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('B_main', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='person', + name='user', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/B_main/migrations/0003_person_보일지여부_alter_person_사진.py b/B_main/migrations/0003_person_보일지여부_alter_person_사진.py new file mode 100644 index 0000000..638dddc --- /dev/null +++ b/B_main/migrations/0003_person_보일지여부_alter_person_사진.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.16 on 2025-08-01 07:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('B_main', '0002_person_user'), + ] + + operations = [ + migrations.AddField( + model_name='person', + name='보일지여부', + field=models.BooleanField(default=True, verbose_name='메인페이지 표시'), + ), + migrations.AlterField( + model_name='person', + name='사진', + field=models.ImageField(blank=True, default='static/B_main/images/default_user.png', upload_to='profile_photos/'), + ), + ] diff --git a/B_main/migrations/0004_person_회원가입상태_alter_person_사진.py b/B_main/migrations/0004_person_회원가입상태_alter_person_사진.py new file mode 100644 index 0000000..21b1ce9 --- /dev/null +++ b/B_main/migrations/0004_person_회원가입상태_alter_person_사진.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.16 on 2025-08-01 11:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('B_main', '0003_person_보일지여부_alter_person_사진'), + ] + + operations = [ + migrations.AddField( + model_name='person', + name='회원가입상태', + field=models.CharField(choices=[('미가입', '미가입'), ('회원가입', '회원가입'), ('탈퇴', '탈퇴')], default='미가입', max_length=10, verbose_name='회원가입 상태'), + ), + migrations.AlterField( + model_name='person', + name='사진', + field=models.ImageField(blank=True, default='profile_photos/default_user.png', upload_to='profile_photos/'), + ), + ] diff --git a/B_main/migrations/0005_person_keyword1_person_keyword2_person_keyword3.py b/B_main/migrations/0005_person_keyword1_person_keyword2_person_keyword3.py new file mode 100644 index 0000000..1e849e8 --- /dev/null +++ b/B_main/migrations/0005_person_keyword1_person_keyword2_person_keyword3.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.16 on 2025-08-01 12:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('B_main', '0004_person_회원가입상태_alter_person_사진'), + ] + + operations = [ + migrations.AddField( + model_name='person', + name='keyword1', + field=models.CharField(blank=True, help_text='첫 번째 키워드를 입력하세요 (예: 회계감사)', max_length=50, null=True, verbose_name='키워드1'), + ), + migrations.AddField( + model_name='person', + name='keyword2', + field=models.CharField(blank=True, help_text='두 번째 키워드를 입력하세요 (예: 잡자재)', max_length=50, null=True, verbose_name='키워드2'), + ), + migrations.AddField( + model_name='person', + name='keyword3', + field=models.CharField(blank=True, help_text='세 번째 키워드를 입력하세요 (예: 기획)', max_length=50, null=True, verbose_name='키워드3'), + ), + ] diff --git a/B_main/migrations/0006_person_모든사람보기권한.py b/B_main/migrations/0006_person_모든사람보기권한.py new file mode 100644 index 0000000..4791fc6 --- /dev/null +++ b/B_main/migrations/0006_person_모든사람보기권한.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.16 on 2025-08-02 13:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('B_main', '0005_person_keyword1_person_keyword2_person_keyword3'), + ] + + operations = [ + migrations.AddField( + model_name='person', + name='모든사람보기권한', + field=models.BooleanField(default=False, help_text='True인 경우 모든 사람을 볼 수 있고, False인 경우 회원가입한 사람만 볼 수 있습니다.', verbose_name='모든 사람 보기 권한'), + ), + ] diff --git a/B_main/migrations/0007_remove_person_보일지여부.py b/B_main/migrations/0007_remove_person_보일지여부.py new file mode 100644 index 0000000..ac15b2f --- /dev/null +++ b/B_main/migrations/0007_remove_person_보일지여부.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.16 on 2025-08-02 15:30 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('B_main', '0006_person_모든사람보기권한'), + ] + + operations = [ + migrations.RemoveField( + model_name='person', + name='보일지여부', + ), + ] diff --git a/B_main/migrations/0008_remove_person_회원가입상태.py b/B_main/migrations/0008_remove_person_회원가입상태.py new file mode 100644 index 0000000..45cdf2b --- /dev/null +++ b/B_main/migrations/0008_remove_person_회원가입상태.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.16 on 2025-08-02 15:33 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('B_main', '0007_remove_person_보일지여부'), + ] + + operations = [ + migrations.RemoveField( + model_name='person', + name='회원가입상태', + ), + ] diff --git a/B_main/migrations/0009_remove_person_keyword2_remove_person_keyword3_and_more.py b/B_main/migrations/0009_remove_person_keyword2_remove_person_keyword3_and_more.py new file mode 100644 index 0000000..dddba94 --- /dev/null +++ b/B_main/migrations/0009_remove_person_keyword2_remove_person_keyword3_and_more.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.16 on 2025-08-02 15:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('B_main', '0008_remove_person_회원가입상태'), + ] + + operations = [ + migrations.RemoveField( + model_name='person', + name='keyword2', + ), + migrations.RemoveField( + model_name='person', + name='keyword3', + ), + migrations.AlterField( + model_name='person', + name='keyword1', + field=models.CharField(blank=True, help_text='다른 사람들이 당신을 찾을 수 있도록 키워드를 입력하세요 (예: 회계감사)', max_length=50, null=True, verbose_name='검색 키워드'), + ), + ] diff --git a/B_main/migrations/0010_alter_person_options_person_비밀번호설정필요.py b/B_main/migrations/0010_alter_person_options_person_비밀번호설정필요.py new file mode 100644 index 0000000..d5fb4dc --- /dev/null +++ b/B_main/migrations/0010_alter_person_options_person_비밀번호설정필요.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.16 on 2025-08-02 16:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('B_main', '0009_remove_person_keyword2_remove_person_keyword3_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='person', + options={'verbose_name': '사람', 'verbose_name_plural': '사람들'}, + ), + migrations.AddField( + model_name='person', + name='비밀번호설정필요', + field=models.BooleanField(default=False, help_text='True인 경우 사용자가 메인페이지 접근 시 비밀번호 설정 페이지로 리다이렉트됩니다.', verbose_name='비밀번호 설정 필요'), + ), + ] diff --git a/B_main/migrations/__init__.py b/B_main/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/B_main/migrations/__pycache__/0001_initial.cpython-38.pyc b/B_main/migrations/__pycache__/0001_initial.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32449d157cb8c78284ccdf37ba0f573f881d67c0 GIT binary patch literal 959 zcmY*YOK;Oa5Z+xoY3w*{DCKnp36x6}I3k4Fq-m){Ls8p{Wyx~8Ti2-{!4E{aLDODn zZb%@ZoRFxHH~`{U_#u1c6xC-A94NDnq3v28&A0P;X6M`STBV{QSo4inTeoC{ehP4Q z!5*4{H@XRiB8pw)VI6x!Cm1atN~qLBRARUz?Mk{t zAT>kf9BF{mj9=*!h>oA*PGqQ>b1!6QGDjEBp{WdEsDkOGz66;r!%q1$v#ruAePUx> zqgSEvYg}3S*YPSZF0(KSd`=uX&&lFkP3D@F#DTdHGAm}3m%E|kS>cYk&33pFvCy$x z=Q-orQx>iV5i@P*`^@f1AsG;<_XL;fwUkUZM!Y{`Hx?I zN)LOyu)4VVuvL@72?&H0VZ_z((n{;`TC3e`K_yunm=`x4=F+HLtMHPU^o8eC~^ zSz+$Zfu%U6v2wM-*Nc2N&k|gXUdh4MdNO=JIUgQG3CdW6^dTbd43q zb*{BG?a^uq=Z7lq&$wmt&G5yukc zIKtTaj&yPok(@@fj)`<5?+kIu-A{yU=6%H+Z=9$FwS4YyB7&?9^{Lp) zI$4l)vqu)i-5bii8?wRCK5s!cz6mC`rJ)b+gyl)9OsC)PzhJ3Kt2B|u+lo>b%Fvw_ zPE~{i$XEhQCAk2IweDpqB%g){#%Fb&G1F!Ya62i$ z?=bd$mq?3ctRz6sJkcML?KWA#ITu@@xKQis5(b0J{$ROy!)MiW4{b#rNZkLCKe_5wM2zw%4T#_xiB) zW9yJMcK%S0Vh6sbzvaw98{Lk=Jty07A@GjYFo=)$US9xd&*lPDw X8t*yejQ^^cz-z0x_P})T0rts1dT`nB literal 0 HcmV?d00001 diff --git a/B_main/migrations/__pycache__/0003_person_보일지여부_alter_person_사진.cpython-38.pyc b/B_main/migrations/__pycache__/0003_person_보일지여부_alter_person_사진.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de9846af25984a7e985563bf13fc15375b1ab7ef GIT binary patch literal 818 zcmZ8f&1=*^6rX%-HkN$IN@b_j|L}={N+}C-H9UP$%T4GOjKY zjX6BxE(#!^MI_c-E!JIKBZ~xdF!l%-OdA<*4A%g2m2?}w@Gj}<(uu<$^Yf4=LRv8g z7KsfNy1H6u%;6DFP%z?ZKwKTPKywXdjvA@}vqnuYp#j!dhvr!MZS>pdZ=t^h4or;= z6$y0Lfi^FB|DjiA@LxJz8Y*YH8@FRsoS-?`3082JPCNdm2 zk@gVavBH-b%52Yb9x*?uT+$gvIm<5erwZkT&XKozvPUGlfe$P3dmFWd2i_V|E4+o3 z;rz4ZMeh+4+c{6Y)i8?u;nGUgpI5i;U49V{hjV{2od0G)eT8EZtV!&puQK&L6|LeH z3fs{CnhjIaaktMW+O>1@Pjzp(Yswak{3PH#Sg)F|TCReZJ=EGw%qQ*Hn#PBU8KKyk GG5!Go%3LXR>Ct)vbMNcBqZN=iDwOD%zfg#;*n$0#ZXA;q#s$0=R z3*H1@cq>9d!6F{~OXli9YSUvcLeX!MUAhz}%*Xfm&G*jo@NiAR^XubQ`>L)eKcz5u z3>2pE@+(L{0o7Lm)lvh^(o|(m0S)v`1$3%*^sa8{U@R$((mrC9h9+u(=Q1bpqL7Pn z5CQdhOPU5x6@_WM{5}$@SSl!%2CA!CIyE{a8Gx2M6)>O#Wmks^m{5hCo$U8)YLT+Q^uo^9DLo&-v7vU zHnP{d2j4b`<0q8$joGu^Y-dxH3v4=$Zx7#WWp7{o{<4vMc%F5i9(La~v?E!|!RPOV zmQ_$fW&ISInT5VA_1%|;FZP&xm&5nvFw(QqGT{GmFMuH`t#;(KC>Ir=t4``C*kb1~ z@oDHLZBc(j*-FGI37vp8bhOEq=aMj_PLx(X>O(3+7d1UtzZA1*)$=Kd+ffqn$)019 za>^#+&|M!r1whB872gThn5;}x)7Xz3AW5{)7!oGQ!x18Cgkbm-9TQO}u;kz?aej$3FLq6!@+bfXDa$&1fR&W$H!6x>DhC(N5g N8*jbCJ<(2&UQCRBv!#HI7iKeG<~P4@zW2`e_NEZ5?_W3ab1FhV z1USFo4voPMZ-F3)NDeAVrc{zmSwd5Y$V90iqEM-*tSP2K)H!5m$DoT0nWajFTws?A zo*S}6$s^PW7X@{Gr2&k=4t)?5nG!*!Or)G-DpW0M!U0Vbdx%Ojk^tF5^i7Q6NIkFe^gDCWcaTA{_p8${9?vXS&|fOH|O<}g*e+d$Cy0Jr+vBs SmYII9UAOz96rm_jpYj(6bZNu@ literal 0 HcmV?d00001 diff --git a/B_main/migrations/__pycache__/0006_person_모든사람보기권한.cpython-38.pyc b/B_main/migrations/__pycache__/0006_person_모든사람보기권한.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bbe68d4de108e447e20a64771596f87876144a14 GIT binary patch literal 834 zcmZ`%&1(}u6rbJMY&J_OJ*Ws?_9O%(#IGJiq)8<`H5O_w%fh(rOw&#GBkpF^-b5<| zn~ETUR2wP?e&8Wkl^*mTnX4zW+oMORZ+BZ83eGT(`OW*id2iux4h6s@te^shAzh$I7pc$`bj+kyE=j=TR-Q^UM`fDdz*O15;8kb= zQmMQ!bC?4X$~EGcmaAW`R4P|UoB4s~k`=ak&-3YpWBB4Re2L3hKzIf!i9WuMo^Qtw zKSljswDV=SI~acJ?Y($i4?XbS@Z&mxfIi05Y_)c?F>Tyn!Ai$#8*`Rzo7I_F5`x>v^fZq8$N*(04wd_#Nagmb zpERxvD;Ni(U~yl{NrDLT#2;xt^)^ZpFVL3hHoY=kNZ*sbB6S{>o9#})EnK;l{mk{` Ku|e_yLpoblB7AH?RGm`XuJOd_UA3F!m|>pZ%r}ceLplPJ zQ3Wx=ls&{0j@x8cX%eP3kdb+TK4f5_yLKzEHf-MuS7hq1`~&5vi5Y@%+`>(TtL=;g zX6inHSYsIg)kJ^w8_gg>D^b_7y=W9L3x&cs?eHMvl2MxV&iBtfarDTRi};ouKa zx*l1MY?2Y<9quvit=K#a2!M-$1@8Kre9q<$6S@z3(-&N!AP4otyf1-Qo-`LjINL1O zi>3Km#hm5g+J@gT8@A(E#cG{KP&QMh=I<{}AUr{XWXlil|CI8btw4bl8fYlDW)ict`A|QTTFp>IKWkfF%g?jr0du8g$kBRJ5`haPP8cLpn$`>+%sZ9U? literal 0 HcmV?d00001 diff --git a/B_main/migrations/__pycache__/0008_remove_person_회원가입상태.cpython-38.pyc b/B_main/migrations/__pycache__/0008_remove_person_회원가입상태.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6bf4da432e7240485a2326294fe6748acd68f62 GIT binary patch literal 573 zcmZWlJ4*vW5Z>Lpobikb{sg;gVmd`cd||4@U`P`VmeXD0#d~?@33icar(!4gYDKUR z6f1wpwql{4rL8)9hZuEWhk5Nc-#3lHK^=iTjUBE97@J zQ5iA9lwHIWj@zWGGznAd$jCjz95S%bUAq-po3`&otFqK(~2^ z(6V|w0^@Hd`^o9i%S|V_*h{)QZ{0J%(_No&hkBOFg(`j3=o5LG7AO@ur7#dX9O43{ z8?oicA(=5AaF20s)#g!10ACC&r(70o?l7VIusXZJ6$*!-pSpJoqWMX)5dmRyzBW6z zR4tneJX+uM1G8>Bjx}4U(HPohCe{2orU{BC{wV&hsu*mC3T)BfMRIXoA)jha#RQWN o^)u~N4Mp^`w&k__B3nu6r}ZDASu_maMSd!~!L&q;q|}1)1!*;@L;wH) literal 0 HcmV?d00001 diff --git a/B_main/migrations/__pycache__/0009_remove_person_keyword2_remove_person_keyword3_and_more.cpython-38.pyc b/B_main/migrations/__pycache__/0009_remove_person_keyword2_remove_person_keyword3_and_more.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b64fce943ca2cd80cde9599e1d956612ba2864d GIT binary patch literal 909 zcmZuvOHUL*5bk*`v%_MDH$Bb8$6g5HMTs#yj9dhX$YnA~XQ11CFpqU+hsO=sCBXnn zq9%k_FoA=K2cjM@F&_LGef7jn@7ct}NA)ZS8awH)tGX&x^;Ok)M~8vneCc^syrLlV zRVZhV3Q8Z`=rRC7L~@ZQS&}DPvV?{Zk%>}AM4?hysVSC1)G?ITj=&e?WoCG#Lg-XW zfgiDq7ZB=3lfrcNbn# z3uut`RgL6Um4aerNe7g1j%9~W@_+CtQ_nWWQ zdHs-Yyfm-!^`%}D>TJBPHxAYyZvLnT^2D70jd%0B_VhGBUSryX>-bYFTcmCUSLtjO z=Hbj@m->Zj5sHspm*u8uI1xlNC6rgfHV75!08D#SqAnT98%)Qk6=P;#3{#9<3UUtP z$Fbvz76^^d3iS!~CrdO6Wf7qLLE7KDA`CP7-6{=JIfOMsEIU|qS~@cV=yPjJ-k|V; zs6fE?nZaIrJc8-igQNWe_eX~8J2aZA1{Hg(g$-O6Py5-%{_9yvnkq?D&PCi%g{a bQ<|yqlu}E&2vM1StJTTdq8pKDSf}zEdlxL= literal 0 HcmV?d00001 diff --git a/B_main/migrations/__pycache__/0010_alter_person_options_person_비밀번호설정필요.cpython-38.pyc b/B_main/migrations/__pycache__/0010_alter_person_options_person_비밀번호설정필요.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c70759791ab873dac862e26241cc245e3c196d63 GIT binary patch literal 977 zcmZuwOG_J36rNX-NgAc4i#}GfDr6&RyXlK!V$qckux^IHW%OKQ;ymJ=jIBFqv#_;_ zr3xY{7NjNMLZna&?H{?@uIfydU6nr0ov9j158ONFea`*PnTejBoP;!cXLaV9DoJ03 z&`lZ&{doKvc!316Ejh9&JBq2u(vSoSs4WRpD%aKLs;PoDE)`PW&{rxbJm=J^%xcy= z*XL=+18VzI!qiP!6#DV_WxS|l${?8v$W_@?sa8*k0-!-^TY>b7f|@jHGBi`yL5D2l z;KGUu`4zRQm|5t-;=MdOd|DM-NSBFY)m-y_u~@uI7hL$EE}D)*qHGe zF(koN%n^QSg@=pb(NbsgIBM-jdmEjNRvE3lh9J45XTY0{@nmcbx2<>C>?2rz*UPfgdT4I_md z*Ga4m`6ek(S}Q zN<3foJeyi>tl~KjCzUK*TVhwxA-=u`$PL`9O!zqX%D`BuJUTK|c|`r$rq`&9*KFG= z4Udrk&6Q+dD(9P9DHd-Qi6t62U7WuIJQcg)?w(;^V}XhPN34x6hOMj0Z!M+Cimdd4iLvqgEeR*Gas5Gpg`kg0BwU86f&Gh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6wo*(xTqIJKxa z#yP;z$=}aC#wE2lyClCLCO9)EC(+S2AifkR9pe + + + + + 회원가입 | 신라 AMP + + + + + + + + + + \ No newline at end of file diff --git a/B_main/templates/B_main/main(darkmode).htm b/B_main/templates/B_main/main(darkmode).htm new file mode 100644 index 0000000..96b907e --- /dev/null +++ b/B_main/templates/B_main/main(darkmode).htm @@ -0,0 +1,39 @@ +{% load static %} + + + + + + 신라대학교 AMP 제8기 + + + + +
+

신라대학교 AMP 제8기

+ + +
+ +
+ + +
+ {% for person in people %} + {% include 'B_main/partials/card.htm' %} + {% endfor %} +
+
+ + diff --git a/B_main/templates/B_main/main.htm b/B_main/templates/B_main/main.htm new file mode 100644 index 0000000..4190ec4 --- /dev/null +++ b/B_main/templates/B_main/main.htm @@ -0,0 +1,211 @@ +{% load static %} + + + + + + 신라대학교 AMP 제8기 + + + + + + + +
+ +
+

신라대학교 AMP 제8기

+
+ {% if user.is_authenticated %} +
+ + + +
+ +
+ + + + +
+ + + + +
+ +
+ {% elif request.session.authenticated %} + 로그아웃 + {% else %} + 로그인 + 회원가입 + {% endif %} +
+
+ + + +
+ +
+ + +
+ {% for person in people %} + {% include 'B_main/partials/card.htm' %} + {% endfor %} +
+
+ + + + \ No newline at end of file diff --git a/B_main/templates/B_main/partials/card.htm b/B_main/templates/B_main/partials/card.htm new file mode 100644 index 0000000..51e9871 --- /dev/null +++ b/B_main/templates/B_main/partials/card.htm @@ -0,0 +1,123 @@ +{% load static %} +
+ +
+ {% if person.사진 and person.사진.url and 'media/' in person.사진.url %} + {{ person.이름 }} + {% else %} + {{ person.이름 }} + {% endif %} + {% if person.이름 %} + + 📇연락처저장 + + {% endif %} +
+ + +
+
+
+

+ {{ person.이름 }} + {% if person.생년월일 %} + ({{ person.생년월일|date:"Y년 m월 d일" }}) + {% endif %} +

+
+ {% if person.TITLE %} + {% if person.TITLE == '회장' or person.TITLE == '수석부회장' or person.TITLE == '선임상임부회장' or person.TITLE == '고문회장'%} + + {{ person.TITLE }} + + {% elif "경조국" in person.TITLE or "기획사무국" in person.TITLE or "홍보국" in person.TITLE or "친교문화국" in person.TITLE or "재무국" in person.TITLE %} + + {{ person.TITLE }} + + {% else %} + + {{ person.TITLE }} + + {% endif %} + {% endif %} +
+ +
+
+ 소속: + {{ person.소속 }} +
+
+ 직책: + {{ person.직책 }} +
+
+ 연락처: + {{ person.연락처 }} +
+
+ {% if person.이름 == '허남식' %} + 이메일: + {% else %} + 주소: + {% endif %} + {{ person.주소 }} +
+
+
+
+ + + + + \ No newline at end of file diff --git a/B_main/templates/B_main/partials/card_list.htm b/B_main/templates/B_main/partials/card_list.htm new file mode 100644 index 0000000..b77e2ac --- /dev/null +++ b/B_main/templates/B_main/partials/card_list.htm @@ -0,0 +1,4 @@ +{% load static %} +{% for person in people %} + {% include 'B_main/partials/card.htm' %} +{% endfor %} diff --git a/B_main/templates/B_main/password.htm b/B_main/templates/B_main/password.htm new file mode 100644 index 0000000..c4f2050 --- /dev/null +++ b/B_main/templates/B_main/password.htm @@ -0,0 +1,62 @@ + + + + + + Enter Access Code | 신라 AMP + + + + + + +
+
+

신라 AMP

+

Enter access code to continue

+
+ +
+ {% csrf_token %} +
+ +
+ +
+ + +
+ + + + + diff --git a/B_main/templates/B_main/phone_verification.html b/B_main/templates/B_main/phone_verification.html new file mode 100644 index 0000000..75ffde1 --- /dev/null +++ b/B_main/templates/B_main/phone_verification.html @@ -0,0 +1,114 @@ + + + + + + 전화번호 인증 | 신라 AMP + + + + + + +
+
+

신라 AMP

+

전화번호 인증

+
+ +
+ {% csrf_token %} + + {% if form.errors %} +
+ {% for field, errors in form.errors.items %} + {% for error in errors %} + {{ error }} + {% endfor %} + {% endfor %} +
+ {% endif %} + +
+ + {{ form.name }} +
+ +
+ + {{ form.phone }} +
+ +
+ + {{ form.verification_code }} +
+ + +
+ + +
+ + + + + \ No newline at end of file diff --git a/B_main/templates/B_main/profile_form.htm b/B_main/templates/B_main/profile_form.htm new file mode 100644 index 0000000..84ddb04 --- /dev/null +++ b/B_main/templates/B_main/profile_form.htm @@ -0,0 +1,217 @@ + + + + + + 프로필 수정 | 신라 AMP + + + + + + + +
+
+

신라 AMP

+

프로필 수정

+
+ +
+ {% csrf_token %} + {% if form.errors %} +
+ {% for field, errors in form.errors.items %} + {% for error in errors %} + {{ error }} + {% endfor %} + {% endfor %} +
+ {% endif %} + +
+ + {{ form.이름 }} +
+
+ + {{ form.소속 }} +
+
+ + {{ form.직책 }} +
+
+ + {{ form.연락처 }} +
+
+ + {{ form.주소 }} +
+
+ + {{ form.생년월일 }} +
+
+ + + + + {% if form.instance.사진 and form.instance.사진.url %} +
+ 프로필 사진 미리보기 +
+ {% else %} +
+ 프로필 사진 미리보기 +
+ {% endif %} +
+ + +
+

검색 키워드

+

다른 사람들이 당신을 찾을 수 있도록 키워드를 설정하세요

+ +
+ {{ form.keyword1 }} +
+
+ + +
+ + +
+
+

회원탈퇴

+

+ 탈퇴하시면 로그인이 불가능하며, 개인정보는 보존됩니다. +

+ +
+
+ + +
+
+

비밀번호 변경

+

+ 전화번호 인증을 통해 비밀번호를 변경할 수 있습니다. +

+ + 비밀번호 변경 + +
+
+ + +
+ + + + diff --git a/B_main/templates/B_main/signup.html b/B_main/templates/B_main/signup.html new file mode 100644 index 0000000..ae58352 --- /dev/null +++ b/B_main/templates/B_main/signup.html @@ -0,0 +1,135 @@ + + + + + + 회원가입 | 신라 AMP + + + + + + +
+
+

신라 AMP

+

회원 정보를 입력해 주세요

+
+ + {% if message %} +
{{ message }}
+ {% endif %} + + {% if step == 1 %} +
+ {% csrf_token %} +
+ + {{ form1.name }} +
+
+ +
+ {{ form1.phone }} + +
+ {% if error %} +
{{ error }}
+ {% endif %} +
+
+ +
+ {{ form1.verification_code }} + +
+
+
+ {% elif step == 2 %} +
+ {% csrf_token %} + + {% if form2.errors %} +
+ {% for field, errors in form2.errors.items %} + {% for error in errors %} + {{ error }} + {% endfor %} + {% endfor %} +
+ {% endif %} +
+ + +
+
+ + +
+ +
+ + {{ form2.password1 }} +
+
+ + {{ form2.password2 }} +
+ +
+
+ {{ form2.privacy_agreement }} +
+ +
+
+ {% if form2.privacy_agreement.help_text %} +
+

다음 정보의 공개에 동의합니다:

+
    +
  • 이름
  • +
  • 생년월일
  • +
  • 소속
  • +
  • 직책
  • +
  • 연락처
  • +
  • 주소
  • +
  • 사진
  • +
+

※ 위 정보는 신라 AMP 제8기 수강생들 간에 공유됩니다.

+
+ {% endif %} +
+ + +
+ {% endif %} + +
+ 이미 계정이 있으신가요? 로그인 +
+
+ + \ No newline at end of file diff --git a/B_main/tests.py b/B_main/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/B_main/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/B_main/update_phone_numbers.py b/B_main/update_phone_numbers.py new file mode 100644 index 0000000..4f2b49b --- /dev/null +++ b/B_main/update_phone_numbers.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +""" +기존 Person 데이터의 전화번호를 대시 없는 형태로 업데이트하는 스크립트 +""" + +import os +import sys +import django +import re + +# Django 설정 +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'A_core.settings') +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +django.setup() + +from B_main.models import Person + +def format_phone_number(phone): + """전화번호에서 숫자만 추출하여 반환""" + if not phone: + return phone + # 숫자만 추출 + numbers = re.sub(r'[^0-9]', '', phone) + + # 11자리인 경우에만 반환 + if len(numbers) == 11 and numbers.startswith('010'): + return numbers + elif len(numbers) == 10 and numbers.startswith('010'): + return numbers + + return phone + +def update_phone_numbers(): + """기존 Person 데이터의 전화번호를 대시 없는 형태로 업데이트""" + print("=" * 60) + print("Person 데이터 전화번호 업데이트") + print("=" * 60) + + # 모든 Person 데이터 조회 + persons = Person.objects.all() + + updated_count = 0 + for person in persons: + if person.연락처: + old_phone = person.연락처 + new_phone = format_phone_number(old_phone) + + if old_phone != new_phone: + print(f"업데이트: {person.이름} - {old_phone} → {new_phone}") + person.연락처 = new_phone + person.save() + updated_count += 1 + else: + print(f"변경 없음: {person.이름} - {old_phone}") + else: + print(f"전화번호 없음: {person.이름}") + + print(f"\n총 {updated_count}개의 전화번호가 업데이트되었습니다.") + + # 중복 확인 + print("\n중복 전화번호 확인:") + phone_counts = {} + for person in Person.objects.all(): + if person.연락처: + phone_counts[person.연락처] = phone_counts.get(person.연락처, 0) + 1 + + duplicates = {phone: count for phone, count in phone_counts.items() if count > 1} + if duplicates: + print("중복된 전화번호 발견:") + for phone, count in duplicates.items(): + print(f" {phone}: {count}개") + persons_with_phone = Person.objects.filter(연락처=phone) + for person in persons_with_phone: + print(f" - {person.이름} (ID: {person.id}, 회원가입상태: {person.회원가입상태})") + else: + print("중복된 전화번호가 없습니다.") + +if __name__ == '__main__': + update_phone_numbers() \ No newline at end of file diff --git a/B_main/urls.py b/B_main/urls.py new file mode 100644 index 0000000..abb0a0d --- /dev/null +++ b/B_main/urls.py @@ -0,0 +1,19 @@ +from django.urls import path +from . import views +from django.conf import settings +from django.conf.urls.static import static + +urlpatterns = [ + path('', views.main, name='main'), + path('vcard//', views.vcard_download, name='vcard_download'), + path('search/', views.search_people, name='search_people'), + path('password/', views.password_required, name='password_required'), + path('logout/', views.logout_view, name='logout'), + path('my-profile/', views.my_profile, name='my_profile'), + path('withdraw/', views.withdraw, name='withdraw'), + path('session_logout/', views.session_logout, name='session_logout'), + path('signup/', views.signup_view, name='signup'), +] + +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/B_main/views.py b/B_main/views.py new file mode 100644 index 0000000..c580781 --- /dev/null +++ b/B_main/views.py @@ -0,0 +1,373 @@ +from django.shortcuts import render, redirect +from django.http import HttpResponse, JsonResponse +from urllib.parse import unquote +from .models import Person +from django.db import models +from django.contrib.auth.decorators import login_required +from .forms import PersonForm, Step1PhoneForm, Step2AccountForm +from .models import Person +from django.shortcuts import get_object_or_404 +from django.db.models import Q, Case, When, Value, IntegerField +from django.contrib.auth import login, logout +import random +import json + +def password_required(request): + PASSWORD = '1110' # 실제 비밀번호 + + # 디버깅을 위한 로그 + print(f"[DEBUG] password_required - user.is_authenticated: {request.user.is_authenticated}") + print(f"[DEBUG] password_required - user: {request.user}") + + # 로그인이 된 사용자는 바로 메인 페이지로 리다이렉트 + if request.user.is_authenticated: + next_url = request.GET.get("next", "/") + if not next_url: + next_url = "/" + print(f"[DEBUG] User is authenticated, redirecting to: {next_url}") + return redirect(next_url) + + if request.method == "POST": + entered_password = request.POST.get("password") + if entered_password == PASSWORD: + request.session["authenticated"] = True + next_url = request.POST.get("next", "/") + + if not next_url: + next_url = "/" + + return redirect(next_url) + else: + return render(request, "B_main/password.htm", {"error": "Incorrect password. Please try again."}) + + # GET 요청 시 비밀번호 입력 폼 렌더링 + next_url = request.GET.get("next", "/") + return render(request, "B_main/password.htm", {"next": next_url}) + + +# 인증 검사 함수 +def check_authentication(request): + # 디버깅을 위한 로그 + print(f"[DEBUG] check_authentication - user.is_authenticated: {request.user.is_authenticated}") + print(f"[DEBUG] check_authentication - session.authenticated: {request.session.get('authenticated')}") + print(f"[DEBUG] check_authentication - user: {request.user}") + + # 로그인이 된 사용자는 인증 통과 + if request.user.is_authenticated: + print(f"[DEBUG] User is authenticated, allowing access") + return None + + # 세션 인증이 된 사용자도 통과 + if request.session.get("authenticated"): + print(f"[DEBUG] Session is authenticated, allowing access") + return None + + # 둘 다 안 된 경우에만 비밀번호 페이지로 리다이렉트 + print(f"[DEBUG] No authentication found, redirecting to password page") + return redirect(f"/accounts/login/?next={request.path}") + + + +def main(request): + auth_check = check_authentication(request) + if auth_check: + return auth_check + + # 현재 사용자의 Person 정보 가져오기 + current_user_person = None + if request.user.is_authenticated: + try: + current_user_person = Person.objects.get(user=request.user) + except Person.DoesNotExist: + pass + + # 기본 필터: 이름이 있는 사람들 + base_filter = Person.objects.filter( + 이름__isnull=False + ).exclude( + 이름__exact='' + ) + + # 현재 사용자의 권한에 따라 추가 필터 적용 + if current_user_person and not current_user_person.모든사람보기권한: + # 모든사람보기권한이 False인 경우 회원가입한 사람만 표시 (user가 있는 사람들) + base_filter = base_filter.filter(user__isnull=False) + print(f"[DEBUG] 회원가입자만 표시 모드: {current_user_person.이름}") + else: + print(f"[DEBUG] 모든 사람 표시 모드") + + # 순서가 있는 항목을 먼저 보여주고, 나머지는 가나다순으로 정렬 + people = base_filter.annotate( + sequence_order=Case( + When(SEQUENCE__isnull=True, then=Value(1)), + default=Value(0), + output_field=IntegerField(), + ) + ).order_by('sequence_order', 'SEQUENCE', '이름') + + print(f"[DEBUG] 메인 페이지 표시: {people.count()}명") + for person in people: + status = "회원가입" if person.user else "미가입" + print(f"[DEBUG] - {person.이름} (상태: {status})") + + return render(request, 'B_main/main.htm', {'people': people}) + + + +def search_people(request): + auth_check = check_authentication(request) + if auth_check: + return auth_check + + query = request.GET.get('q', '') + print(f"[DEBUG] 검색 쿼리: '{query}'") + + # 현재 사용자의 Person 정보 가져오기 + current_user_person = None + if request.user.is_authenticated: + try: + current_user_person = Person.objects.get(user=request.user) + except Person.DoesNotExist: + pass + + # 기본 필터: 모든 사람 + base_filter = Person.objects.all() + + # 현재 사용자의 권한에 따라 추가 필터 적용 + if current_user_person and not current_user_person.모든사람보기권한: + # 모든사람보기권한이 False인 경우 회원가입한 사람만 표시 (user가 있는 사람들) + base_filter = base_filter.filter(user__isnull=False) + print(f"[DEBUG] 검색 - 회원가입자만 표시 모드: {current_user_person.이름}") + else: + print(f"[DEBUG] 검색 - 모든 사람 표시 모드") + + if query: + # 이름, 소속, 직책, 키워드로 검색 + # 순서가 있는 항목을 먼저 보여주고, 나머지는 가나다순으로 정렬 + people = base_filter.filter( + Q(이름__icontains=query) | + Q(소속__icontains=query) | + Q(TITLE__icontains=query) | + Q(직책__icontains=query) | + Q(keyword1__icontains=query) | + Q(생년월일__icontains=query) + ).filter( + 이름__isnull=False + ).exclude( + 이름__exact='' + ).annotate( + sequence_order=Case( + When(SEQUENCE__isnull=True, then=Value(1)), + default=Value(0), + output_field=IntegerField(), + ) + ).order_by('sequence_order', 'SEQUENCE', '이름') + print(f"[DEBUG] 검색 결과: {people.count()}명") + for person in people: + print(f"[DEBUG] - {person.이름} (소속: {person.소속}, 직책: {person.직책})") + else: + # 순서가 있는 항목을 먼저 보여주고, 나머지는 가나다순으로 정렬 + people = base_filter.filter( + 이름__isnull=False + ).exclude( + 이름__exact='' + ).annotate( + sequence_order=Case( + When(SEQUENCE__isnull=True, then=Value(1)), + default=Value(0), + output_field=IntegerField(), + ) + ).order_by('sequence_order', 'SEQUENCE', '이름') + print(f"[DEBUG] 전체 목록: {people.count()}명") + + return render(request, 'B_main/partials/card_list.htm', {'people': people}) + +def vcard_download(request, name): + auth_check = check_authentication(request) + if auth_check: + return auth_check + + name = unquote(name) + if not name: + return HttpResponse("Invalid name", status=400) + + person = get_object_or_404(Person, 이름=name) + + vcard_content = f"""BEGIN:VCARD +VERSION:3.0 +N:{person.이름};;;; +FN:{person.이름} +ORG:{person.소속} +TITLE:{person.직책} +TEL;CELL:{person.연락처} +ADR:;;{person.주소} +END:VCARD +""" + + response = HttpResponse(vcard_content, content_type='text/vcard') + response['Content-Disposition'] = f'attachment; filename="{person.이름}.vcf"' + return response + + + + + + +def logout_view(request): + request.session.flush() + return redirect('/password/') + + + + +@login_required +def my_profile(request): + try: + person = Person.objects.get(user=request.user) + except Person.DoesNotExist: + person = None + + if request.method == 'POST': + form = PersonForm(request.POST, instance=person) + if form.is_valid(): + person = form.save(commit=False) + person.user = request.user + person.save() + return redirect('main') # or any success page + else: + form = PersonForm(instance=person) + + return render(request, 'B_main/profile_form.htm', {'form': form}) + + +from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.http import require_http_methods + +@login_required +@csrf_exempt +@require_http_methods(["POST"]) +def withdraw(request): + """회원탈퇴 뷰""" + try: + # 현재 사용자의 Person 정보 가져오기 + person = Person.objects.get(user=request.user) + + # User 연결 해제 + person.user = None + person.save() + + # User 객체 삭제 (전화번호 계정 삭제) + user_phone = request.user.username + request.user.delete() + + # 로그아웃 + logout(request) + + print(f"[DEBUG] 회원탈퇴 완료: {user_phone} (User 삭제, Person 연결 해제)") + return JsonResponse({'success': True}) + except Person.DoesNotExist: + return JsonResponse({'success': False, 'error': 'Person 정보를 찾을 수 없습니다.'}) + except Exception as e: + return JsonResponse({'success': False, 'error': str(e)}) + + +def session_logout(request): + try: + del request.session['authenticated'] + except KeyError: + pass + return redirect('/') + +def signup_view(request): + import random + from .forms import is_allowed_person + from django.contrib.auth import login + + # GET 요청 시 세션 초기화 (새로운 회원가입 시작) + # 단, 인증번호 확인 후 리다이렉트된 경우는 세션 유지 + if request.method == 'GET' and not request.session.get('signup_verified'): + for key in ['signup_code', 'signup_name', 'signup_phone', 'signup_verified', 'signup_step']: + request.session.pop(key, None) + request.session['signup_step'] = 1 + request.session['signup_verified'] = False + + step = request.session.get('signup_step', 1) + name = request.session.get('signup_name') + phone = request.session.get('signup_phone') + code_sent = bool(request.session.get('signup_code')) + verified = request.session.get('signup_verified', False) + + # 1단계: 이름, 전화번호, 인증번호 + if step == 1: + if request.method == 'POST': + form = Step1PhoneForm(request.POST) + action = request.POST.get('action') + if action == 'send_code': + if form.is_valid(): + name = form.cleaned_data['name'] + phone = form.cleaned_data['phone'] + + # 폼 검증에서 이미 허가되지 않은 사용자 체크를 했으므로 여기서는 제거 + code = str(random.randint(100000, 999999)) + request.session['signup_code'] = code + request.session['signup_name'] = name + request.session['signup_phone'] = phone + request.session['signup_verified'] = False + print(f"[DEBUG] 인증번호 발송: {name} ({phone}) - {code}") + return render(request, 'B_main/signup.html', { + 'step': 1, 'form1': form, 'code_sent': True, 'message': '인증번호가 발송되었습니다.' + }) + else: + # 폼 에러 메시지 확인 + error_message = '입력 정보를 확인해주세요.' + if form.errors: + # 첫 번째 에러 메시지 사용 + for field_errors in form.errors.values(): + if field_errors: + error_message = field_errors[0] + break + + return render(request, 'B_main/signup.html', { + 'step': 1, 'form1': form, 'code_sent': False, + 'error': error_message + }) + elif action == 'verify_code': + if form.is_valid(): + verification_code = form.cleaned_data['verification_code'] + session_code = request.session.get('signup_code') + if verification_code and verification_code == session_code: + # 인증 성공 + request.session['signup_verified'] = True + request.session['signup_step'] = 2 + return redirect('signup') + else: + return render(request, 'B_main/signup.html', { + 'step': 1, 'form1': form, 'code_sent': True, 'error': '인증번호가 올바르지 않습니다.' + }) + else: + return render(request, 'B_main/signup.html', {'step': 1, 'form1': form, 'code_sent': code_sent}) + else: + form = Step1PhoneForm() + return render(request, 'B_main/signup.html', {'step': 1, 'form1': form, 'code_sent': False}) + + # 2단계: 이메일, 비밀번호, 비밀번호 확인 + if step == 2 and verified and name and phone: + if request.method == 'POST': + form2 = Step2AccountForm(request.POST) + if form2.is_valid(): + user = form2.save(name, phone, request) + login(request, user, backend='django.contrib.auth.backends.ModelBackend') + # 세션 정리 + for key in ['signup_code', 'signup_name', 'signup_phone', 'signup_verified', 'signup_step']: + request.session.pop(key, None) + return redirect('main') + else: + return render(request, 'B_main/signup.html', {'step': 2, 'form2': form2, 'name': name, 'phone': phone}) + else: + form2 = Step2AccountForm() + return render(request, 'B_main/signup.html', {'step': 2, 'form2': form2, 'name': name, 'phone': phone}) + + # 기본: 1단계로 초기화 + request.session['signup_step'] = 1 + request.session['signup_verified'] = False + return redirect('signup') \ No newline at end of file diff --git a/C_accounts/__init__.py b/C_accounts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/C_accounts/__pycache__/__init__.cpython-38.pyc b/C_accounts/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb84964eb7db84f969f80c5d3676acdaed02ab77 GIT binary patch literal 152 zcmWIL<>g`kg2nFL86f&Gh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6w(*(xTqIJKxa z#yP;z$=}aC#wE2lyClCLCO9)EC(+S2AifkR9pfCIn4FwnnpaXB6Ca2KczG$)edCrXCP((08j8E^#A|> literal 0 HcmV?d00001 diff --git a/C_accounts/__pycache__/admin.cpython-38.pyc b/C_accounts/__pycache__/admin.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c2e6b110598b72c0ab43801b5cee842bf718941 GIT binary patch literal 193 zcmWIL<>g`kg2nFL8BRd@F^Gc(44TX@fuanW zjJH@5Q*tx&{4|-O_)@YG^V0M6lJoOQiZYXmKnAR2C}IXuVB(jRvsFxJacWUtFZF*!NEG_RyM25h)qLFFwDo80`A(wtN~ LMxb$@ftUdRW<)PS literal 0 HcmV?d00001 diff --git a/C_accounts/__pycache__/apps.cpython-38.pyc b/C_accounts/__pycache__/apps.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36cc13a3eb2bb3524b3dd77815639709ee74e26d GIT binary patch literal 592 zcmZ8d%}&BV5Z-NzB0@|^ylLXKm)$cmBTl8MRsshVn6bd-%lni;P`y(CDC=GX!A3HJkB}bJk;EfxTj2 zEBc-?sF7xVNcRvOAiAmpZC%#r?2s3&QFYMKau zT{U|_#6DI(r(~Ch?QOkh;%OqaajnuZN>6gS#5>FP@cB6pE~1f9xru^--@WViqHATI z=4loUW38osI}iniqb*$|v&?weqO9dg!lI5Wh9+WsdHiolugd7-`gI`^Ia5NE4Iyyf zLesw~#B(9_Cej8qk%gWMiB}Ml>I@FWa_Uiyy94{=vk_<;L%Y+#Hz3 a&1t|Yvco_%Ink8$%k@ZCbi8Vrru_>e0gP4v literal 0 HcmV?d00001 diff --git a/C_accounts/__pycache__/forms.cpython-38.pyc b/C_accounts/__pycache__/forms.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0880a85d8408c74d41b71876e1c20401cfb36c39 GIT binary patch literal 7303 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 GW&i-^A})pi literal 0 HcmV?d00001 diff --git a/C_accounts/__pycache__/signals.cpython-38.pyc b/C_accounts/__pycache__/signals.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c321f9ecfdf89ff5803f1a43d358806c5c73c1b6 GIT binary patch literal 970 zcmZ`%%}*0S6rb7M?zV-hF@gp!J$SH*DPFt~VgxiEjM#(_aMNV7-5tu7{c>g(m4H!O zxL6WRBsM~gM39(xD8!@uBYX8EUH$D5}!I2|MYe)D%WUBuW*V zvZ>Ti6{~48IfZhm-%u6haFp^r7HCXgBw!>dF^IKDC}3%tb@p>1>>9JmE-|&xhlpGt zlzFbf8J?~L0Dhkb@Uar8r5gdN;EJ#e@>$$Q{@}7ukxbkZ!4$V-Q(6}=nnCVO2?dFT zG%aGZB4M<2F_6u~vb2esLKSX2gKi;HUXfl%v*#e?y%+PWx|G!S{p|PhjC*!@4Mxk z9LkUpH-EIh*1B-59F_;gb%HD^DsVWbOT?0#%uL1eEmJ%tL219;%^ou zO){s4cEG}GaOyv_vhmKlY~0vs?>0uVlR1TQ3X$TKD#Qp_lqr)y^QdM}q6egF&V{zE zxw=E5B=6Hfse8{1;s=)JMhTx9XrN0? z^E*SK`P7@UY@$G6HUtWZf*@l-=*PmToJfDteHSntgy>|hPrYJRbhqr~%~Ia+Oaf(l z<*5J}(fQM`#e6+5<|&`MXsG8kJU5_LDX)jYeD64wAVIyX19$H1k^WbdkE)vK&cW#k g{^%V(ofkQy26(UJDDVnxavOui`a$*Mp1|Gy0?GO(N&o-= literal 0 HcmV?d00001 diff --git a/C_accounts/__pycache__/urls.cpython-38.pyc b/C_accounts/__pycache__/urls.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6535d0a5fffe8e110a1c5a2d09e2afe486c8f35 GIT binary patch literal 590 zcmZ{h%}xR_5XW0~f#nlp>jzg``00A12n7Wh_D1jA~V#~E~ zZM!z7Q0CUE3>Bz8Sx6321!`NWBUPcXr6!UCtu3{YYS1yXD7$s&-jV*n2Ja(%s_oRz zmK*v7<%f{3m3I6f5P6cRqLoT94lcwUEGqrJpSL zPOK>xcNa#HKfb7NvSTHCT*5% Jt4+84{R03FurB}r literal 0 HcmV?d00001 diff --git a/C_accounts/__pycache__/views.cpython-38.pyc b/C_accounts/__pycache__/views.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e9b0fb43dfc2b0a2d21f3918373d21d8ac9136c GIT binary patch literal 5809 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 P@I)5>ljie~qni33g1L=r literal 0 HcmV?d00001 diff --git a/C_accounts/admin.py b/C_accounts/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/C_accounts/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/C_accounts/apps.py b/C_accounts/apps.py new file mode 100644 index 0000000..1936998 --- /dev/null +++ b/C_accounts/apps.py @@ -0,0 +1,10 @@ +from django.apps import AppConfig + + +class CAccountsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'C_accounts' + + def ready(self): + import C_accounts.signals + diff --git a/C_accounts/forms.py b/C_accounts/forms.py new file mode 100644 index 0000000..56ea6be --- /dev/null +++ b/C_accounts/forms.py @@ -0,0 +1,269 @@ +from django import forms +from django.contrib.auth import get_user_model +from B_main.models import Person # 또는 Person 모델이 정의된 경로로 import +import random +import re + +User = get_user_model() + +def format_phone_number(phone): + """전화번호에서 대시 제거""" + return re.sub(r'[^0-9]', '', phone) + +class CustomFileInput(forms.FileInput): + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + # "Currently:" 텍스트 제거 + if 'help_text' in context: + context['help_text'] = '' + return context + +class ProfileFullEditForm(forms.ModelForm): + # 통합된 이름 필드 (편집 불가능) + full_name = forms.CharField( + label="이름", + required=False, + widget=forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-600 bg-opacity-80 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'readonly': 'readonly', + 'placeholder': '이름' + }) + ) + + class Meta: + model = Person + fields = [ + '소속', '직책', '주소', '사진', 'keyword1' + ] + widgets = { + '소속': forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '소속' + }), + '직책': forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '직책' + }), + '주소': forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '주소' + }), + '사진': CustomFileInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'accept': 'image/*' + }), + 'keyword1': forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '검색 키워드 (예: 회계감사)' + }), + } + + def __init__(self, *args, **kwargs): + self.user = kwargs.pop('user') + super().__init__(*args, **kwargs) + + # 통합된 이름 설정 (first_name + last_name) + full_name = f"{self.user.first_name or ''} {self.user.last_name or ''}".strip() + self.fields['full_name'].initial = full_name + + # Person 모델 필드 초기값 설정 (기존 인스턴스가 있는 경우) + if self.instance and self.instance.pk: + # 기존 Person 인스턴스의 데이터로 초기화 + for field_name in self.fields: + if field_name == 'full_name': + continue + if hasattr(self.instance, field_name): + self.fields[field_name].initial = getattr(self.instance, field_name) + + def save(self, commit=True): + # Person 모델 저장 (User 모델은 수정하지 않음) + if commit: + instance = super().save(commit=False) + instance.user = self.user + instance.save() + + return self.user + +# 모드1: 비밀번호 찾기 폼 +class PasswordResetStep1Form(forms.Form): + """비밀번호 찾기 1단계: 전화번호 인증""" + phone = forms.CharField( + max_length=11, + label='전화번호', + widget=forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '01012345678' + }) + ) + verification_code = forms.CharField( + max_length=6, + label='인증번호', + required=False, + widget=forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '6자리 인증번호' + }) + ) + + def clean_phone(self): + phone = self.cleaned_data.get('phone') + if phone: + # 전화번호 포맷팅 적용 (대시 제거) + formatted_phone = format_phone_number(phone) + + # 해당 전화번호로 가입된 사용자가 있는지 확인 + try: + user = User.objects.get(username=formatted_phone) + return formatted_phone + except User.DoesNotExist: + raise forms.ValidationError('등록되지 않은 전화번호입니다.') + return phone + +# 모드2: 로그인 상태 비밀번호 변경 폼 +class PasswordChangeLoggedInForm(forms.Form): + """로그인 상태에서 비밀번호 변경""" + current_password = forms.CharField( + label='현재 비밀번호', + widget=forms.PasswordInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '현재 비밀번호' + }) + ) + new_password1 = forms.CharField( + label='새 비밀번호', + widget=forms.PasswordInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '새 비밀번호' + }) + ) + new_password2 = forms.CharField( + label='새 비밀번호 확인', + widget=forms.PasswordInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '새 비밀번호 확인' + }) + ) + + def __init__(self, *args, **kwargs): + self.user = kwargs.pop('user', None) + super().__init__(*args, **kwargs) + + def clean_current_password(self): + current_password = self.cleaned_data.get('current_password') + if self.user and not self.user.check_password(current_password): + raise forms.ValidationError('현재 비밀번호가 올바르지 않습니다.') + return current_password + + def clean(self): + cleaned_data = super().clean() + password1 = cleaned_data.get('new_password1') + password2 = cleaned_data.get('new_password2') + + if password1 and password2 and password1 != password2: + raise forms.ValidationError('새 비밀번호가 일치하지 않습니다.') + + if password1 and len(password1) < 8: + raise forms.ValidationError('비밀번호는 최소 8자 이상이어야 합니다.') + + return cleaned_data + +# 모드3: 강제 비밀번호 설정 폼 +class ForcePasswordSetForm(forms.Form): + """강제 비밀번호 설정""" + new_password1 = forms.CharField( + label='새 비밀번호', + widget=forms.PasswordInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '새 비밀번호' + }) + ) + new_password2 = forms.CharField( + label='새 비밀번호 확인', + widget=forms.PasswordInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '새 비밀번호 확인' + }) + ) + + def clean(self): + cleaned_data = super().clean() + password1 = cleaned_data.get('new_password1') + password2 = cleaned_data.get('new_password2') + + if password1 and password2 and password1 != password2: + raise forms.ValidationError('비밀번호가 일치하지 않습니다.') + + if password1 and len(password1) < 8: + raise forms.ValidationError('비밀번호는 최소 8자 이상이어야 합니다.') + + return cleaned_data + +# 기존 폼들 (유지) +class PasswordChangeStep1Form(forms.Form): + """비밀번호 변경 1단계: 전화번호 인증""" + phone = forms.CharField( + max_length=11, + label='전화번호', + widget=forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '01012345678' + }) + ) + verification_code = forms.CharField( + max_length=6, + label='인증번호', + required=False, + widget=forms.TextInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '6자리 인증번호' + }) + ) + + def clean(self): + cleaned_data = super().clean() + phone = cleaned_data.get('phone') + + if phone: + # 전화번호 포맷팅 적용 (대시 제거) + formatted_phone = format_phone_number(phone) + cleaned_data['phone'] = formatted_phone + + # 현재 로그인한 사용자의 전화번호와 일치하는지 확인 + if not self.user or self.user.username != formatted_phone: + raise forms.ValidationError('등록된 전화번호와 일치하지 않습니다.') + + return cleaned_data + + def __init__(self, *args, **kwargs): + self.user = kwargs.pop('user', None) + super().__init__(*args, **kwargs) + +class PasswordChangeStep2Form(forms.Form): + """비밀번호 변경 2단계: 새 비밀번호 입력""" + new_password1 = forms.CharField( + label='새 비밀번호', + widget=forms.PasswordInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '새 비밀번호' + }) + ) + new_password2 = forms.CharField( + label='새 비밀번호 확인', + widget=forms.PasswordInput(attrs={ + 'class': 'w-full px-4 py-3 rounded-xl bg-gray-700 bg-opacity-80 text-white placeholder-gray-400 border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 transition', + 'placeholder': '새 비밀번호 확인' + }) + ) + + def clean(self): + cleaned_data = super().clean() + password1 = cleaned_data.get('new_password1') + password2 = cleaned_data.get('new_password2') + + if password1 and password2 and password1 != password2: + raise forms.ValidationError('비밀번호가 일치하지 않습니다.') + + if password1 and len(password1) < 8: + raise forms.ValidationError('비밀번호는 최소 8자 이상이어야 합니다.') + + return cleaned_data diff --git a/C_accounts/middleware.py b/C_accounts/middleware.py new file mode 100644 index 0000000..f09c0c2 --- /dev/null +++ b/C_accounts/middleware.py @@ -0,0 +1,25 @@ +from django.shortcuts import redirect +from django.urls import reverse +from B_main.models import Person + +class ForcePasswordSetMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + # 로그인한 사용자이고 비밀번호 설정이 필요한 경우 + if request.user.is_authenticated: + try: + person = Person.objects.get(user=request.user) + if person.비밀번호설정필요: + # 현재 URL이 강제 비밀번호 설정 페이지가 아닌 경우에만 리다이렉트 + current_path = request.path + force_password_set_path = reverse('accounts:force_password_set') + + if current_path != force_password_set_path and not current_path.startswith('/admin/'): + return redirect('accounts:force_password_set') + except Person.DoesNotExist: + pass + + response = self.get_response(request) + return response \ No newline at end of file diff --git a/C_accounts/migrations/__init__.py b/C_accounts/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/C_accounts/migrations/__pycache__/__init__.cpython-38.pyc b/C_accounts/migrations/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..29e6a1212994c35309b6fcc014581d30b61c0472 GIT binary patch literal 163 zcmWIL<>g`kf+g + + + + + 비밀번호 설정 | 신라대학교 AMP 제8기 + + + + + + + + + +
+ +
+
+

신라대학교 AMP 제8기

+
+ {% if user.is_authenticated %} +
+ + + +
+ + + + + + +
+ +
+ {% elif request.session.authenticated %} + 로그아웃 + {% else %} + 로그인 + 회원가입 + {% endif %} +
+
+
+ + +
+
+
+

비밀번호 설정

+

보안을 위해 비밀번호를 설정해주세요

+
+ +
+ {% csrf_token %} + {% if form.errors %} +
+ {% for field, errors in form.errors.items %} + {% for error in errors %} + {{ error }} + {% endfor %} + {% endfor %} +
+ {% endif %} + +
+ + {{ form.new_password1 }} + {% if form.new_password1.errors %} +

{{ form.new_password1.errors.0 }}

+ {% endif %} +
+ +
+ + {{ form.new_password2 }} + {% if form.new_password2.errors %} +

{{ form.new_password2.errors.0 }}

+ {% endif %} +
+ + +
+
+
+
+ + + \ No newline at end of file diff --git a/C_accounts/templates/C_accounts/password_change.html b/C_accounts/templates/C_accounts/password_change.html new file mode 100644 index 0000000..5a1d17d --- /dev/null +++ b/C_accounts/templates/C_accounts/password_change.html @@ -0,0 +1,142 @@ +{% extends "base.htm" %} + +{% block content %} + + +
+
+
+

신라 AMP

+

비밀번호 변경

+
+ + {% if step == 1 %} + +
+ {% csrf_token %} + {% if error %} +
{{ error }}
+ {% endif %} + {% if message %} +
{{ message }}
+ {% endif %} + +
+ + {{ form1.phone }} + {% if form1.phone.errors %} +

{{ form1.phone.errors.0 }}

+ {% endif %} +
+ + {% if code_sent %} +
+ + {{ form1.verification_code }} + {% if form1.verification_code.errors %} +

{{ form1.verification_code.errors.0 }}

+ {% endif %} +
+ {% endif %} + +
+ {% if not code_sent %} + + {% else %} + + {% endif %} +
+
+ + {% elif step == 2 %} + +
+ {% csrf_token %} + {% if form2.errors %} +
+ {% for field, errors in form2.errors.items %} + {% for error in errors %} + {{ error }} + {% endfor %} + {% endfor %} +
+ {% endif %} + +
+

전화번호: {{ phone }}

+
+ +
+ + {{ form2.new_password1 }} + {% if form2.new_password1.errors %} +

{{ form2.new_password1.errors.0 }}

+ {% endif %} +
+ +
+ + {{ form2.new_password2 }} + {% if form2.new_password2.errors %} +

{{ form2.new_password2.errors.0 }}

+ {% endif %} +
+ + +
+ {% endif %} + + +
+
+{% endblock %} \ No newline at end of file diff --git a/C_accounts/templates/C_accounts/password_change_logged_in.html b/C_accounts/templates/C_accounts/password_change_logged_in.html new file mode 100644 index 0000000..229324a --- /dev/null +++ b/C_accounts/templates/C_accounts/password_change_logged_in.html @@ -0,0 +1,197 @@ +{% load static %} + + + + + + 비밀번호 변경 | 신라대학교 AMP 제8기 + + + + + + + + + +
+
+ +
+

신라대학교 AMP 제8기

+
+ {% if user.is_authenticated %} +
+ + + +
+ + + + + + +
+ +
+ {% elif request.session.authenticated %} + 로그아웃 + {% else %} + 로그인 + 회원가입 + {% endif %} +
+
+ + +
+
+
+

비밀번호 변경

+

현재 비밀번호를 입력하고 새 비밀번호를 설정하세요

+
+ +
+ {% csrf_token %} + {% if form.errors %} +
+ {% for field, errors in form.errors.items %} + {% for error in errors %} + {{ error }} + {% endfor %} + {% endfor %} +
+ {% endif %} + +
+ + {{ form.current_password }} + {% if form.current_password.errors %} +

{{ form.current_password.errors.0 }}

+ {% endif %} +
+ +
+ + {{ form.new_password1 }} + {% if form.new_password1.errors %} +

{{ form.new_password1.errors.0 }}

+ {% endif %} +
+ +
+ + {{ form.new_password2 }} + {% if form.new_password2.errors %} +

{{ form.new_password2.errors.0 }}

+ {% endif %} +
+ + +
+ + +
+
+
+
+ + \ No newline at end of file diff --git a/C_accounts/templates/C_accounts/password_reset.html b/C_accounts/templates/C_accounts/password_reset.html new file mode 100644 index 0000000..5f109fc --- /dev/null +++ b/C_accounts/templates/C_accounts/password_reset.html @@ -0,0 +1,122 @@ + + + + + + 전화번호 찾기 | 신라 AMP + + + + + + +
+
+

신라 AMP

+

전화번호 인증을 통해 비밀번호를 재설정하세요

+
+ + {% if step == 1 %} + +
+ {% csrf_token %} + {% if error %} +
{{ error }}
+ {% endif %} + {% if message %} +
{{ message }}
+ {% endif %} + +
+ + {{ form1.phone }} + {% if form1.phone.errors %} +

{{ form1.phone.errors.0 }}

+ {% endif %} +
+ + {% if code_sent %} +
+ + {{ form1.verification_code }} + {% if form1.verification_code.errors %} +

{{ form1.verification_code.errors.0 }}

+ {% endif %} +
+ {% endif %} + +
+ {% if not code_sent %} + + {% else %} + + {% endif %} +
+
+ + {% elif step == 2 %} + +
+ {% csrf_token %} + {% if form2.errors %} +
+ {% for field, errors in form2.errors.items %} + {% for error in errors %} + {{ error }} + {% endfor %} + {% endfor %} +
+ {% endif %} + +
+

전화번호: {{ phone }}

+
+ +
+ + {{ form2.new_password1 }} + {% if form2.new_password1.errors %} +

{{ form2.new_password1.errors.0 }}

+ {% endif %} +
+ +
+ + {{ form2.new_password2 }} + {% if form2.new_password2.errors %} +

{{ form2.new_password2.errors.0 }}

+ {% endif %} +
+ + +
+ {% endif %} + + +
+ + \ No newline at end of file diff --git a/C_accounts/templates/C_accounts/profile_edit.html b/C_accounts/templates/C_accounts/profile_edit.html new file mode 100644 index 0000000..92c8f3c --- /dev/null +++ b/C_accounts/templates/C_accounts/profile_edit.html @@ -0,0 +1,350 @@ +{% load static %} + + + + + + 프로필 수정 | 신라대학교 AMP 제8기 + + + + + + + + + +
+
+ +
+

신라대학교 AMP 제8기

+
+ {% if user.is_authenticated %} +
+ + + +
+ +
+ + + + +
+ + + + +
+ +
+ {% elif request.session.authenticated %} + 로그아웃 + {% else %} + 로그인 + 회원가입 + {% endif %} +
+
+ + +
+
+
+

프로필 수정

+

개인 정보를 수정하세요

+
+ + {% if messages %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endif %} + +
+ {% csrf_token %} + {% if form.errors %} +
+ {% for field, errors in form.errors.items %} + {% for error in errors %} + {{ error }} + {% endfor %} + {% endfor %} +
+ {% endif %} + + +
+ + {{ form.full_name }} +
+ +
+ + +
+ + {% if form.instance.생년월일 %} +
+ + +
+ {% endif %} + + {% if form.instance.TITLE %} +
+ + +
+ {% endif %} + + +
+ + {{ form.소속 }} +
+
+ + {{ form.직책 }} +
+
+ + {{ form.주소 }} +
+
+ + {{ form.사진 }} + + {% if form.instance.사진 and form.instance.사진.url %} +
+ 프로필 사진 미리보기 +
+ {% else %} +
+ 프로필 사진 미리보기 +
+ {% endif %} +
+ + +
+

검색 키워드

+

다른 사람들이 당신을 찾을 수 있도록 키워드를 설정하세요

+ +
+ {{ form.keyword1 }} +
+
+ + +
+ + + + + +
+
+
+
+ + + + diff --git a/C_accounts/templates/base.htm b/C_accounts/templates/base.htm new file mode 100644 index 0000000..aec5c22 --- /dev/null +++ b/C_accounts/templates/base.htm @@ -0,0 +1,46 @@ + + + + + + {% block title %}신라 AMP{% endblock %} + + + + + + + +
+

신라대학교 AMP 제8기

+ {% if user.is_authenticated %} +
+ {{ user.email }}님 | + 로그아웃 +
+ {% else %} + + {% endif %} +
+ + +
+ {% block content %}{% endblock %} +
+ + + diff --git a/C_accounts/templatetags/__init__.py b/C_accounts/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/C_accounts/templatetags/__pycache__/__init__.cpython-38.pyc b/C_accounts/templatetags/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5be934e956b354d9d07b8869d43e31e61588d72 GIT binary patch literal 165 zcmWIL<>g`k0$s1}3=sVoL?8o3AjbiSi&=m~3PUi1CZpdI z;~e1VfGsUcMUB-D{C}s?dJ5$z(g7`=O;m>|w3TX}j zmZ*k}ODr)bYYdt}=E5yMWY%gg)MX}rmb?-+AucSXP}~KDF&{|#zx`zC2^|}u4NZrG zi@WTKUJCPM+Zi26rP$zhn9t#wra3E$c0RR+ZXH`z8Ctfw$xEp$NF2`=+FK00#f^88 zC6SAG&8AhW{lx9v$Op~z!o^xt(g4jxfAkG?V`>NXJtsbGT0U3e413`S;DE%WhkpU< CJ!gsl literal 0 HcmV?d00001 diff --git a/C_accounts/templatetags/form_filters.py b/C_accounts/templatetags/form_filters.py new file mode 100644 index 0000000..1d19690 --- /dev/null +++ b/C_accounts/templatetags/form_filters.py @@ -0,0 +1,7 @@ +from django import template + +register = template.Library() + +@register.filter(name='add_class') +def add_class(field, css): + return field.as_widget(attrs={"class": css}) diff --git a/C_accounts/tests.py b/C_accounts/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/C_accounts/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/C_accounts/urls.py b/C_accounts/urls.py new file mode 100644 index 0000000..c3e9313 --- /dev/null +++ b/C_accounts/urls.py @@ -0,0 +1,13 @@ +# C_accounts/urls.py +from django.urls import path +from . import views + +app_name = 'accounts' + +urlpatterns = [ + path('profile_edit/', views.profile_edit, name='custom_profile_edit'), + path('password_change/', views.password_change, name='password_change'), + path('password_reset/', views.password_reset, name='password_reset'), + path('password_change_logged_in/', views.password_change_logged_in, name='password_change_logged_in'), + path('force_password_set/', views.force_password_set, name='force_password_set'), +] diff --git a/C_accounts/views.py b/C_accounts/views.py new file mode 100644 index 0000000..c6879ea --- /dev/null +++ b/C_accounts/views.py @@ -0,0 +1,271 @@ +from django.shortcuts import render, redirect +from django.contrib.auth.decorators import login_required +from django.contrib import messages +from django.contrib.auth import get_user_model +from django.http import JsonResponse +from .forms import ( + ProfileFullEditForm, PasswordChangeStep1Form, PasswordChangeStep2Form, + PasswordResetStep1Form, PasswordChangeLoggedInForm, ForcePasswordSetForm +) +from B_main.models import Person +import random + +User = get_user_model() + +@login_required +def profile_edit(request): + """프로필 편집 뷰""" + # 현재 사용자의 Person 인스턴스 가져오기 + try: + person = Person.objects.get(user=request.user) + except Person.DoesNotExist: + # Person 인스턴스가 없으면 새로 생성 + person = Person.objects.create(user=request.user) + + if request.method == 'POST': + form = ProfileFullEditForm(request.POST, request.FILES, user=request.user, instance=person) + if form.is_valid(): + form.save() + messages.success(request, '프로필이 성공적으로 업데이트되었습니다.') + return redirect('accounts:custom_profile_edit') + else: + form = ProfileFullEditForm(user=request.user, instance=person) + + return render(request, 'C_accounts/profile_edit.html', {'form': form}) + +@login_required +def password_change(request): + """비밀번호 변경 뷰 (2단계 프로세스)""" + # 세션 초기화 + if 'password_change_step' not in request.session: + request.session['password_change_step'] = 1 + request.session['password_change_code'] = None + request.session['password_change_phone'] = None + request.session['password_change_verified'] = False + + step = request.session.get('password_change_step', 1) + code_sent = request.session.get('password_change_code') is not None + verified = request.session.get('password_change_verified', False) + phone = request.session.get('password_change_phone') + error = None + message = None + + if step == 1: + if request.method == 'POST': + action = request.POST.get('action') + + if action == 'send_code': + form1 = PasswordChangeStep1Form(request.POST, user=request.user) + if form1.is_valid(): + phone = form1.cleaned_data['phone'] + # 인증번호 생성 (실제로는 SMS 발송) + verification_code = str(random.randint(100000, 999999)) + print(f"[DEBUG] 인증번호: {verification_code}") # 실제로는 SMS 발송 + + request.session['password_change_code'] = verification_code + request.session['password_change_phone'] = phone + request.session['password_change_step'] = 1 + message = '인증번호가 발송되었습니다.' + code_sent = True + else: + error = '전화번호를 확인해주세요.' + elif action == 'verify_code': + form1 = PasswordChangeStep1Form(request.POST, user=request.user) + if form1.is_valid(): + input_code = form1.cleaned_data['verification_code'] + stored_code = request.session.get('password_change_code') + + if input_code == stored_code: + request.session['password_change_verified'] = True + request.session['password_change_step'] = 2 + return redirect('accounts:password_change') + else: + error = '인증번호가 일치하지 않습니다.' + else: + error = '인증번호를 확인해주세요.' + else: + form1 = PasswordChangeStep1Form(user=request.user) + + return render(request, 'C_accounts/password_change.html', { + 'step': 1, 'form1': form1, 'code_sent': code_sent, 'error': error, 'message': message + }) + + elif step == 2 and verified and phone: + if request.method == 'POST': + form2 = PasswordChangeStep2Form(request.POST) + if form2.is_valid(): + new_password = form2.cleaned_data['new_password1'] + request.user.set_password(new_password) + request.user.save() + + # 세션 정리 + del request.session['password_change_step'] + del request.session['password_change_code'] + del request.session['password_change_phone'] + del request.session['password_change_verified'] + + messages.success(request, '비밀번호가 성공적으로 변경되었습니다.') + return redirect('accounts:custom_profile_edit') + else: + return render(request, 'C_accounts/password_change.html', { + 'step': 2, 'form2': form2, 'phone': phone + }) + else: + form2 = PasswordChangeStep2Form() + return render(request, 'C_accounts/password_change.html', { + 'step': 2, 'form2': form2, 'phone': phone + }) + + # 기본: 1단계로 초기화 + request.session['password_change_step'] = 1 + request.session['password_change_verified'] = False + return redirect('accounts:password_change') + +# 모드1: 비밀번호 찾기 (로그인하지 않은 상태) +def password_reset(request): + """비밀번호 찾기 뷰""" + # 세션 초기화 + if 'password_reset_step' not in request.session: + request.session['password_reset_step'] = 1 + request.session['password_reset_code'] = None + request.session['password_reset_phone'] = None + request.session['password_reset_verified'] = False + + step = request.session.get('password_reset_step', 1) + code_sent = request.session.get('password_reset_code') is not None + verified = request.session.get('password_reset_verified', False) + phone = request.session.get('password_reset_phone') + error = None + message = None + + if step == 1: + if request.method == 'POST': + action = request.POST.get('action') + + if action == 'send_code': + form1 = PasswordResetStep1Form(request.POST) + if form1.is_valid(): + phone = form1.cleaned_data['phone'] + # 인증번호 생성 (실제로는 SMS 발송) + verification_code = str(random.randint(100000, 999999)) + print(f"[DEBUG] 비밀번호 찾기 인증번호: {verification_code}") # 실제로는 SMS 발송 + + request.session['password_reset_code'] = verification_code + request.session['password_reset_phone'] = phone + request.session['password_reset_step'] = 1 + message = '인증번호가 발송되었습니다.' + code_sent = True + else: + error = '전화번호를 확인해주세요.' + elif action == 'verify_code': + form1 = PasswordResetStep1Form(request.POST) + if form1.is_valid(): + input_code = form1.cleaned_data['verification_code'] + stored_code = request.session.get('password_reset_code') + + if input_code == stored_code: + request.session['password_reset_verified'] = True + request.session['password_reset_step'] = 2 + return redirect('accounts:password_reset') + else: + error = '인증번호가 일치하지 않습니다.' + else: + error = '인증번호를 확인해주세요.' + else: + form1 = PasswordResetStep1Form() + + return render(request, 'C_accounts/password_reset.html', { + 'step': 1, 'form1': form1, 'code_sent': code_sent, 'error': error, 'message': message + }) + + elif step == 2 and verified and phone: + if request.method == 'POST': + form2 = ForcePasswordSetForm(request.POST) + if form2.is_valid(): + new_password = form2.cleaned_data['new_password1'] + # 해당 전화번호의 사용자 찾기 + try: + user = User.objects.get(username=phone) + user.set_password(new_password) + user.save() + + # 세션 정리 + del request.session['password_reset_step'] + del request.session['password_reset_code'] + del request.session['password_reset_phone'] + del request.session['password_reset_verified'] + + messages.success(request, '비밀번호가 성공적으로 재설정되었습니다. 새 비밀번호로 로그인해주세요.') + return redirect('account_login') + except User.DoesNotExist: + error = '사용자를 찾을 수 없습니다.' + else: + return render(request, 'C_accounts/password_reset.html', { + 'step': 2, 'form2': form2, 'phone': phone + }) + else: + form2 = ForcePasswordSetForm() + return render(request, 'C_accounts/password_reset.html', { + 'step': 2, 'form2': form2, 'phone': phone + }) + + # 기본: 1단계로 초기화 + request.session['password_reset_step'] = 1 + request.session['password_reset_verified'] = False + return redirect('accounts:password_reset') + +# 모드2: 로그인 상태에서 비밀번호 변경 +@login_required +def password_change_logged_in(request): + """로그인 상태에서 비밀번호 변경 뷰""" + if request.method == 'POST': + form = PasswordChangeLoggedInForm(request.POST, user=request.user) + if form.is_valid(): + new_password = form.cleaned_data['new_password1'] + request.user.set_password(new_password) + request.user.save() + + messages.success(request, '비밀번호가 성공적으로 변경되었습니다.') + return redirect('accounts:custom_profile_edit') + else: + form = PasswordChangeLoggedInForm(user=request.user) + + return render(request, 'C_accounts/password_change_logged_in.html', {'form': form}) + +# 모드3: 강제 비밀번호 설정 +@login_required +def force_password_set(request): + """강제 비밀번호 설정 뷰""" + # 현재 사용자의 Person 인스턴스 확인 + try: + person = Person.objects.get(user=request.user) + if not person.비밀번호설정필요: + return redirect('main') + except Person.DoesNotExist: + return redirect('main') + + if request.method == 'POST': + form = ForcePasswordSetForm(request.POST) + if form.is_valid(): + new_password = form.cleaned_data['new_password1'] + request.user.set_password(new_password) + request.user.save() + + # 비밀번호 설정 필요 플래그 해제 + person.비밀번호설정필요 = False + person.save() + + # 로그아웃 처리 + from django.contrib.auth import logout + logout(request) + + # 로그아웃 후 세션에 메시지 저장 (로그인 페이지에서 표시) + request.session['password_set_message'] = '비밀번호가 성공적으로 설정되었습니다. 새 비밀번호로 로그인해주세요.' + + return redirect('account_login') + else: + form = ForcePasswordSetForm() + + return render(request, 'C_accounts/force_password_set.html', {'form': form}) + + diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..f6823ff3dfd72fb2c2f49486f7f1f03b95c47b2d GIT binary patch literal 331776 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

*`Tjmnw=WW4T@vNh zXyV;dHs$6AUs#PVe~`GjXn#W3^k}^C{G~IFKg;q4)|$$js!HrXv*%;9=(@3_zPY5P z2!Bk9!uZ4ZSYWhsgNR5x%{Artyr-e0w4{N^8a?}<1$~|LX;Y>LJ;n6b1~zr&HRVm^ z1yxn;<}XW%(I>sZefd6nK{mbod7q~%93I>t$rCs5(r%mI4U#+<{l(s_M*>Be(H9J5 z4Euw=L7(3r9uGx2$2RyhcA%oF(^$v$4mJQ3{1Jd6jsJA3>Z&-s-==XfYM@*(Vb&4HLx^&^ZY zeK$YS$Sm@Gm|8Hp@8HRrK-MEgMVT)>#g_Sp@cJQtI=XT7e#j^PQENHLbaFKdo0cmL zCHY5Q=Qk{W+3>hmq5*)&6HjN16K&!j6AJ#E|htLn3KVYeYz3eOt^pmZv zz*J<^*NGl|`#cf+kYvhJbgZPP(sSTIMPqGEXG=jo_Oq|_%~#389-(!d{Th(`O{hoHgMrbwi6qsB3(Z}%J3McWG3u~z*=a&tVf=E zF7wp49oC{~Wih(`+T8lW z?ZlnT;jsp#3;-N`dhoR>Y*pzI!;`wQVJ$n-^66};rSOoWr!)deIs)Vi;2_k;=j_v zjl8a@UQY4yl0!}$ej)Rml2T45=i)a%FoeWIYO9G0rxKU0CoV&TODpRy-646d9#75L z!Xo4B!TA|T?MX=On-0ci--Xngj+J;C+Z&rosv#c1%gJ6|u{o18iDpn&w}@wTAu+o| z^B8~UZesb&+MTx(D@!uij9;BwyL;CoAM%KayLVEs#kyr|<<3u=Yz0alO-|K%$cyF&Z?>+i(RuEtm2UB7rfvGR&1F^gf}g;tSzs;G)yjTcO1bjUXT)+s#0m}!;xR@4K=@8U0CJm~r8zEjKq$$lRYzRwjOXJ~6kLm|cm#b06bhyEPlXxd6c%!+@|w$j9Uy%UHx(=5>=3 ztYR?6Y|ZPKx$C4368#Dhll7Cw6R(}|NW2JZ3IT0?{w7un!=c`>@x%SYy=$wlpu2~# z=n$atB;H(3T(}W^VtyQY^E7xLD|yCJWD3p9zDpCmc#W*dg;U1bSG~MutA{QM8sv#U^O9qS)fJ@MKrP!m!RNf01+nKwuI#z;D%|#-uW>|l(w9(lUuB| zmBiv4j34-c4k%tuz^-G9Q^)!*eK7`~o*!X*&lCHj*|zBdV=z~h#f317SgDvz z2uyjrjr%WNtV=RqKH?cLk)8}~iN6V2)5xzqw=-4lQ77tfMw z4n2H)ei)L}A<5?*OXbXQEEV!4z}JOWU`Z3N-d;a*E&jdR9!L-zlg2qDsDWciP%Zlp z=@YvcF60+KeUAjk3v2^Al-jW3S?G~i!_wTaP;%$P=30K<79@RWk%Wyd)>fC|t4oF~ zH?k%2BJb5XU7!zQK_y;2XSzM4A=huSHL!REtkI67rPz^-euyoY1H<0;&WFH{_~aA& zb8XcldvciqzLs0H7>&P7Q-<9V?uG0Xbo>BwZ?0Kvm?})8lETX}9>tXC(EJdT=vlH6 z4>Bc~*~;lJS~X^la+GBcxcZ7C%g|yXX5MSTA(0Imz!G9I3GtJ9Liu ze^5C16JS`}pl|{w!2j1Z=3bmwb=Ww=Ys81+XBUih%59e9EweAi%R}=)@KOfhm0Roa z)(R=)9QN_W_z%t)4uL1}{nLqMa$+b9@Leo2=8I|Y#T*`j3|~x_IL;V_2WC2HaPMOW zGx*uENUZ*dA5a?88vWFK9~e`?n9mXt+|aAX;Z)*4ucHgS*cpLFmPy9aSY3Fs(8`}piK zj0ZFg)(gIeZnGn55;h1pf15SU78G)BaCZ~$-!T@3N3`4#-IIbd)-&VGJKtm3mIrOt z)_%A|cJ*DH+*YZ5k6cok!yC7a%@ZdQa*rG=^0I;{luUn#ohgmY3aerL<$0PyQRWVi z?ZpXsah6EW&&H7?yZp)_rut}^+jnp)!v;T$VP!r5hDBPmk6VhjxVEU2csZ9G;oyZc6B`K)==q~q(t9MeEa^&BwI z8(87Y$TJru6~l}lVwgC49bU)VbS!=(<7!6!P~OelzsuEgzO(ml_CCMoSF-8)tb^bap(%q(M>F z&!35Z?*=x5wIAM%{{SHd(pcimi{MNa|Yp1Zhh^wo! z@Tb>LE`wKznMH@{O0q0^6_MMz*m1&HG=d^9NU*WcVTr>DI)(2-p$pdG0Xi40EF5Hv z6~#Ir%aSLb-XDF&mL)l`i_!AJO42n%4VI6uzdCQ9)1atHk+Kkt(A z!7avdd;QEAoVyJ-0RtfWfd6cr5Jdrp7C}(p0g$_puwmZ-S1fwr$fT+DmadFl5dcPq9M@szPT1V}ln3!>DbRtJO2( z?3q|A&33Q`eV|~*0(N3ULzs1onuiy5z+yb|4jwg47reS6I!=EO)@F@>G<8q> z{Q1Pf8M3^~w<#3I7NJMv@sc`yJD;b>{7TYn%iugrxTZFX*OryX4@G2{L?Fj z2acelZ2NQ)kJB{80XA#x2BUy^0M9%r0m79=d`Q5toU=To(<@uVX?E{ibrb`*Jd2)8 zhd_@tgKd7pA_Ut@S6~n5IDP7P;`^u87p}AQi={^!1`L{lMM;x@b!NEFii{|dfUnJi zpcQZ03HBWgK!LUxBhV1W*xZQ+a$3869NzX;myblscw;hGVohM4S>vPy!3S`zK;Y#x z%*T1-U`9Lh1)2pYFl0|o9+@N~nu=(Fj@S{uMf|W1q$2AlmmmUSo5z_BD{OTU#}-FS z08VzQzEaEvV~lbBXsj{mhZ)gnnw1-fPA`JZ^`+bKYY0$ZNxXl1{p@{g+Ymgo8auVa z4?`RXfelsW;QJ>oQ9x)3%MuS^iv8YonjeIbPcI>s1MfjirNL&O#FMQPpN%z`JH~+N zM9^ZmcpfVaDhVw#+3VmOBWI^vE$w;I5>!uQz*!>qp>5DtZJsga>fd8p2tB?#8j)A zwZb;XtCGF3X?!eJ2X4IJChU+DxVc0JII<3iqC@RZtdeUF!wFB?5knVc9U)z#K)(zU`3Rt8d_p6u%CP-0j55 z@ub>2B2l`CjE%@4IK?)3+Hg`BN*GDDRbD;rNi1GToLaPkrzva-gvYwwD7=zbHM5K` z=(X1VgHOVLc!whWFjBM;(TV9i_Df@H#J0YHQ;1X7x1NC=(@Tj6L_8_<&!$#26;D3^ z;l{r=kEL>u`OzMVd7fS-vwZ@d%*NhR$er=Yl=RV96$DKV%7{G+^1ue zV2-@gPFoAea>0zC#=YZ5F8MfUIV8zfsE z4wk-EQbuPR=YD^z0&`EgnGAl=OfOURa6S48{5iw;8CeNO_K*T;@P>GkA=?8rqkR-3 zh39dLO?J}C@WKI%m(G1KjN~#boHzFu1?sxRfwMSa0(>@B4pyH**dyw7NDvvmaapl_8pE(wI|u)n`6LHD z*t^!hvq+oGt5%XCMYx6}$k^H_Ybkcj+Qc6%c{Do{=~4P5PT@aX!oezW`i5zxaMYeP zN?^B zq$Qh9EFRcl z&=KI0!ROpcg8XV^Rp9&cGJY_fKAa7qIry?ou_Nd*dlyb4Ej)xFQ>0asJrQqoFwM=n zl97KP@5S7;+_s#Pd;fTE(Vo-Uf1B;!eIx5%<4^9FD*;ymt^`~OxDs$B@H3FW;nNxU z8M(RmFBjm_Z1`!Lyp8Tak8gZ1!Y%{RrXFpKeH$)P$&=6QkBX*?1ZRa#>ju%*x5ND(?jd@lElnucP{F$XdB>)2SBtXx0v2E9bvTLusLav9P z9YU0Zf_Dej-le=5{3?WSn4?Rd0>%!Y8X)jehDwvn(r74mZNO5**tak&Gs|LNz|2n~ zy$49iZ3>E+Xek!2HUUR0gBnT2^fYaAuV^Aa&8DvL;lQDO&+ym83|pE!S|*dFqRd8U}%b>Q@t1cb!t zWyHFTEEKGhjueiOaHoSmPOnBb2S725lATNqf+UOuAmKOXQmiLC9kn^imKU3Z zqzN9Al=R{(lhW;~GV1%)+llwD!IU6?)7W|dk(q zjPD>|8R+u-|-{_HvIX5-|jvfU^)7FB&Fh{3lgd<-&kEIL6WaoQC7?`m2PCaE2tnFH{inazu8KHU%g_*UXF)qTL4A>#g_A*bm?ehA~o9mN7kmv0?+m ztZ!k~Vumr>6eCC^gF6*jN(y7TdRHN^A+ofL#KXd@k={hmRU%EseccrKGTLewpEQt7 zddl+unL9G_gLyy9{oCC7oHugv_6GL+X7-sHqGU9-Ek?F{bt;SSIC`ECDx zTV>{beEbvj6_sLRm|H?H*$MGGfEUgJP;BZsrG;5qiXqIAiQ_ediEPZAq#&0Ftk9NN zo|()t{*SCTFL0LmHhzhRFr*0uo{a~3=u!egyQi;g*2#p$7SqYSF#?*BV_|LK^!F=p z?(W26i}BU-$Pujn3ewgq*uSw(kYjLYB33(C=2gMYeA7;KVij4(^_Oq3C>V?s3*&ku zN_=mzXPPn-v~*^DhSV}r%(#Nz^SDtcu=iVUGU zGDK^H@_L(z56NP4bx51xO~elC&Y5_PJ{2PnDXrXOt_Yu7T0wx5c9)d}in}e}CBGOL z59~B=0`5dPBkah|K)ftVUIjn_TO&5uKr-bvNMC{_#YL4=J2G*}dS}Zq>D)dTBPc03 zx1Hww2Kr#^9@aYH0A9!US3L9`Qsl28q)f3`z`-0GB0(clUeb|mx3w>czW|Kgl%Kd6 zgq7@&I5VBs2V;aNeFh#wuNAVqL+MV+{zGo?_OVwiAZAK|N|y8Eg>~W@8^bI31f3jU z^8_F$6$wPZ4j^dip%f0qnBg5^jh(&?aHEHgCD@A$kOi-9@)^LK(Jj-p!!bgivZKHg z7GLlWzkdqBYuY^M`QU>2uTVSiX5^n8ISd`>f9St;10dbj=76riZ?JSv@x@DRLj`fWd_E9DV3E26q-NeS-ZVRhw!cL2ad=|9*9jF6<;3fgYJiEyRy)c z^x@b&5*5@HctdtBFo9fGXV;hCfB{9O(*ftY@OJ#xUCbCp0tW?PXU+K8bnit@6d^vR zE!kl0s2^lH4aHMLS{6_eIMplUo?KWl&m=;s{DZ|S6aE)sy%-GbVfJ7M5PKi%3Fd;7 z$<9Avo-wO%k8nyd(c=<8s6d{XHqTzfc2I)O5-XI8Bj6|p$1%1xcoqGeY}+36cD2TQo(YH4yW1?LE2nU1E32d1m1 z2BnP=HW#O?aQ27r(9M?EVuUgJV5|#_J`K}i9m$AM*wfi%*j7?_AR&GQs3?j_LBls1 z4Zu}(z-n}div+cz&A^NS8sGE!*ugt<4jBA;R+3b+XSm zM7?^BfZK1buAe%KWsNNphy^Dmgxvy_WNB->!bF=b19ZkhmsTvzqaVQJ5K8>ziy|~P z#S|MH5R2pvF+g?n3$d?5gk&hfCC{*XCTu6Yj$Hz*A`rx!5(f6a0Kc3-oY+Sk!3=Op zemR(DszE-Iy|kRLB*Q{@0K#CW#jU#>M0APzB?ie^!<{p0*WVUB8ta5itI6=%h-%!^ zT{dAhFp`&WpBWbI%{e-L(=D6#ac>QXTDo|`mX9+AgOfLOLVCGaO{`B{WDKKMzKN@^|^wL?glR6 zK+Mp6z^uuFQS4FyV+az`UhOeAYs49!#fnpCaJfcI^GVy_fe6?A7+}-1GZ;Ud;Y4*}swf%h{FL5A6Qk-LLNc z`tGN*{xs|TtOs`e!LH-GzOZv?=Vx}D*|B5$H@5u+x^Tb#`<6g-XS8@v#zRkJM)}O_ z0vNS;_OYaX-o{ z;RjXoq&fDoKClLzZo_yyeEVh?tmsRML-1 z%3)o=4P(7M`iqWd%k26kL;U>&&lV5M2uJXu5dAWHb}sP^hbn1qw%f)53<*a$=_s!r zRulmNwCES3M-8Em{aD7KTOsq~SGr*we9T1nRgLU{pY!vu3S76J3b4U)_++vVs!H}l z)Y0O5`i<@n-y%BL4*e+4A660O=V8Zm$M85E!-Fq@Q^&^PF(OVlEXbUq zfJk!^X=!AVBoa6c@tvreo$y0( z0=njnt_73yqkB~l!Qj3e<Hz^z&J0Md|fIA!TRvPSTNJzrwscVUwst!b-TL zAyKd;Kw*BtCSk^%m8D5Tf&vFnndsv-32h=2MgGD4bR<|~7|Z9ABqG~ZStX$~B$C2w zkl1532~WK+$3G1Ts$l6d7yX<~LbFe)>nq38k%(AfT=Y?!gk~SLnh*p?Ln5fSe8@#V zYm?CIQ(7wR@Ug%|n2Sej5}JLGppHYDUU($FG%otEO+vFz7cv&oEHNG#P+FA_*(5ak zM3S}_Kv*>N@)?_iX5T2gmzpt7mjzy8`TvZ28Tnt!dp|EDw$9f9HSOsqHwi{okVc-^#XanS=QF&;AP)0ZI@7k|};X zM7ecM#ST`P5ke!z3O3DqHVDg_fV<6gpeXi;F#Hc$NYG3Pd@fc5u0()Et%8l_r;vR? zW(rkpSP?bzJ}%5GWdf2HVmCrWMW8ErO)-P4j*=<#3{pbp#ALHdDs<-=@khXrX#VjBN~Q`Lq^ZGUG6TGJhRvK@tR6&3 z5fe6)Z)^C`>WY}PXIO|m2R2)wf61J~G8N{9y(_G#G*#TLMuEkPoFFD|>5f%)4moMc zlAutQ{aZ6X$jTv+2{xXSX6fL@V!|v0MlpXf5GfL}7FTTci(+4a`C(V!Er>OtI#4ZV z^#*2(CY{~icVsUKsEUQ-gy4uk*J0aoppnVRGizl!DkxGa5QL^+aj(lnt0pTd;x7DI zy0}g?CUH}3X=_5tVs4}GpNKsR;nOgNti%dZhjh`5fn$JE1P1c_HT=dH2ul}}upJ2t zfgo*yWB~x2GG=v3FfD`wWyv2UsUEzz84XOyRMs<7<|HuYk=QSSMZzt<=wK0vjg!mk z3rnng3L!#qz0$GtDMN;2yr`M$3Y#I_syvNc3j5CB1|^b^tYT8-1sV4$fiyfp7wxy? zdcYd%aijEVV_#-UfUAflR#1O&4vI_lrkocB9aj;lL@E_^G_E4rTahe+Vfb+13>h<9 zhzTP<6+cagEL_U9ILH2dXhR>yr9>UcNZhFm!%PrsmN7Fb5Nt?NXs93tDB_l6yt#;teF|w`+NYR0c#QI6ge?;fz_!vK zQb^xoV54H}jp=1Ng-q;L)TsmO<0wUvyK@)|sMR_eQnD{oO zr9K-|Aw(|{X%;G&+#evJug((FSYgP|ku|o;%>hJcf?hndE2uoxL>2%lOP9#TEoDki z(Pm2}Jbqvn+$CUuyu}1qeKU#fi7DWXQf5}sGKT{SjJ+6M`i zi)HnL`*29G+oN)bY40~UN{T}1JQT@D&zo~o1-!*D{XDRvbX#G=r5aQodgC^g5?C;+ zqij(Ci%l8_=5j*`%cC&~!=jRTsN-nDpO8$vb%$aN40uS_4v_YZ;VRDuC*cgx)qqRq z33f&9)&>leETN{0n3Org_ZbLmV|kH-fby;cx~4iw?Cw22ge+~c>>BdFDN&OaPizdy zZ2N;T(X;_c^~JsQxkZY4+`CPe<5%Y5_by<)fl)vJom+%J{*0`N++>Vs*=O<%=-^Y|-K5o0d#pdY$xc`HpcmF*eo-km98%W|bKe1^~ThhTzQc#Jxp^`NpB&OM4im3ot|hwt)T*{>NqpEe41| zsGWIvMUC;6y-$`gcaKw*Fx)qAtAJYuX16-q!n(;2v2Nqw9*=PlGWjk+OUO98qyEn6WtcAG zeg`ps;RY2=IzdJk3Jb7>1N4LN50!+oZdZ|%%?%e?feU3&SXG3Rd>7lE}JzC(c2blVDUiA()qtM<6mXu|C{{l`Gfg<-t9as_t$b? z$?eSfhn#QbROLLjcWv*zy>ok8_kMBDpYQn}_nh6+v1fnwUu6Gk_II+M#~p#c-2H32 zU*7$--3PP&D(ly?UdehP>r1=-8gC5v&aUTo9oYGoJAZBG%R9fe^WcuZ+VSf+Sz?`?>AEwEdZFf3xj3w*AVs&TWS>|2FeCGrybp^-OQZ-+`GQ|BEkWp=6ez z;1v=bt7MWQ@_|f{Al4473Mp^=hHl28oIz%HffMZp>qdF0}fB$QV%@LB0dIMl1>k$p>&$dqYj zn6ES>sC*tV#Eje`WUZ-RN#kwEtq9_cP?ED~nlgfXWf zL5Vm>4C$^U31;6!Xs0F-bU+go9_g+miIhaBry&985#S4?yB>VnSo@OfM8Kz`!pj2{ z9vQD>M+!Tq(%peHB)~N29FL4wl0*fGap55i3DV|BxFh40BtcDl^X^7E5>((8d1Snj zBsxP_)BTh*B)H^>b%uvTLcO?i4*`4(*pe-zf2z*aqcQ-C++n-{1S?P9ff*H< zi=vo7Emcng(*oKF3Mcb0JRp|8yd9(l#!LQajF(M}EgOXywt@ID)~|)Y+km^XKMs73 z!hJClAJ1hU7t(wVKs!O9^M7^5?|(G@|8mY?&e5FRd;j;nSN4wX{p#MlJ%6z0tvyqF zO80yw``>4;W`8TYD*JQ0{}bLBFuS{9wLp%R& z=l`_x)t%itg`F8YetXAjJNkCWJGO2Ao$X88gWHwu+qeDhw&iX8+th74GJh}ga^^s$ zmbnwG{g_`|6!zW3e~Gl{5Mcq@XtT98@LwqX$dD$m?vcouAksr@uS#wZS;12zH+ zZ;=q5gq!QxsZCL|UagLEVU*|Ml@ zb7h+S6e!vxU=UWF4L>W{i%hdmKIGMNX?np+Eg`k&E}MjApB$BxhZGt<#*f>g(VaF4 z%|4N&o=Od-5!MONB$y|2_&~LQWcfzw5DiZxxaA9lfmyOhWWvgzm(&0#kZ1rhLX%;P zEE0uCaPKOm(?z{#P#BQFAXy|T)tOj%1@B8pB{7hw5(L0SGi(wKxdOx}kZMUof?Wx? zyl>kSI#J-vozhDsz7(uuiThTPLg5h0qcp{ZbbTPZ0U+NuZ3>=Jg%{5vrILm#RBx6w z?i)6R5QjIox`0$?Itm=nMeaqLLL>@+LsG^WX&kgvIBv$Kkcfg^uuRhjU49d}X`4a@ z1u_qC>eEmNl&j*VYzl=akit$o>+pVXd0^6}P)&-(H`4ThH3FTVuqiZ?A|+Sw1`zqw zPi4qq{Qs8Ynpg?|bE|no73JC+(xiOo92L(mTQW{7`0iG0Y z)TW?0|FHNPf}XgQ#EsY#H0MxUl>SH|E9yfORd~?DHU-T&>|AP#LKL`eh0+9Jn}X&X z6i9QV=>y9HP|uJ}L32)-;gmpE>Vub1n~NK=DQM2w8ccH_Q#lm8P`N>yg65nkmQ&$8 zsSg+ioO%ar3Yv3-B9VAaJq`s&J`BD;Nukl4Q`?(4TXRU5dzm~Q}Cz- zXJl;CP{1s}CjOF5AyCP5mU>P{At1`hyaWMs7@LR zZ07iCwJCJuaSa$!ItmeP7T;nAcaS=_08VL0FdGP=pYd(-pZhY=}6!Mh5QC$%xV; zR9EL9q134XVNKHu-T{K(Q>}x9Qm3$Vsr?hIcUi!_I-Rd^kWlKBV94omhXAId&K6(o zAfePLk)(^B1L27qe|(iqf>(qxlJtzBDhW3CN(Tw0PSbjTG!~@vaQv%qkgywz*s9YR zVe%iap_DsF*juRCCCAi4mJrrPD(IMlgl3;M>vWEdgbfH*wah_6vyV+NwTFhWNGj5^ z?EL@P41Y#`XWp;pJ)S$Bb7OCOZ{wb8dyZsZ*&W~Al=W8DL%YuI{O3FVa!2#_``fo| zdolBOGCv1`AN?yT#tt)t5$YA?Cvgzw9s!^Rm6*DE#;WdQzLp9b2mv+>gb}jf>}L}U zA!vf=m2l|*o3Xl-Ghbp>#?>@a!%s}e1%ANq7AR@m7B4h1bgp7#t zF)!F=2;by;Cald6Y=ayxEpmc<4G?3*9>6cDgafOJ0|y&hW6BcA$$go4rGT_iedIvw5SV5!P{}qyYRNsHaw$M_U!bb4 ztbB(PeTa8q13`wpX5m}tTFFT71ZFd?IFCx{0aE|f^aB_s3Z#)Y>%BB8f< z1z55ar9_h5uz6NuUxLUOu5Yi2BTBkr0O8peAn+ahIYW5%tF1QT`jC(TXbW!|VVJ|M z7a)umiEBV`6Hs>pjPL3h_~NN2JIN0*Yzy$A*g*)8O3he>rITJKtt%YpfG&GKS+fOY z+DLE?7%Y$w1Vgk+mf4`=jgUA@!v$7U1Xnw}D4k0Mk2XQHk{vUVem-^pj1e+-$WlW1 zkj#Omm2-{&i>OTrQGy@TI&q+2@m5*94jMpWTMud5VypZ*>0KYUEb{xdKP+t2gN~;mB z7XLZ)Sds8M3GZYzc(e7G9bIrM2DX-7_(e5`fQ_RIRy@PRYj=5Yv1r&k)vIIh~9kC8uBMb$lpM~YLudMnw(1{qGoo&YD6j#8cUId=UO5l6V%8%uQ>fZzj( zVOL|kCg3e?@Y6)xZ&C0~YE~ET zGFCqmdmOAD0jt)%JFrSj&aD{W?-TbK`29Zi73dV^4|?ywB;#GFxK|-ruXH87-@w$` zs#%o`E~83ZrOr?5EWKMByC5n~UsC~g#xmLr zP9{mQ04}kV}g2k>@4;{q*xp6g_LyNH|JCp_m(NUoqVQL49 z$&(MN-ma}0)fUO0B#kkzeXvrI)^k&f(%292Sktb5HuhOaleU|72b0({NGq1&Z+$>U z0mgM1sjYKlpe~_&Hr4MXC(c#|L>%Z2k%+5Q3+B;>E^A%562HvKD{Hu4iZg}mK{Rsf z*^wm3x+DoY?+;z=@m6FSdJI|VUIxtZB8+dkq!sv zE;4Dz_Q1h8hnJUM!5YLpO!KnP=KAR1(df}uMautYWqpvD_rK(x&&k*`nEjpIt6Bei zXZEg(+s8BR;(s60ualpCXlL~)u^=!sAWm|_gMBmlM4(@nyGJ4uL51(_>K+N zfr_b?f$GxQ$(e%czDQ@OtPEF{DBTr-@kVb2R}}X5jklGS_D&BCHWmzza%yp4dO9ey z$(@~39a>*YI25T4Omlr|QNv7C?O;tu^VCdTTS?V)S$kKgvU5hPt}ZI|Pso)+lD}-E zy12fqT?h_xa&M(H+0r;vQXKHMN4VjEI&XiSR1%sPtEia@luh)i)ioVbTjls@uhi4g zP*<%^O^r%5(#XU}2j5gxGp0luT833&aJ)$i%}nXF-8~HjCDY@_+G|D;f*TB#R#i@m z18R3|yW+2(Xzm(5R-{zbRB;1g-=s3#Uol*+_X({-!^Oe!iI$Op@Qln)bxziJ+q!$k z3+ub5yakN~eQk36QGUE{G*WO>Yp87ETE=EtC)&a#sw#HW^|Xvu&2%+S6f|n#;nrjQ zll38`UZ|KU2O5|a*27AmVqPc}B~igLgq<@ob`DpcY%frKJ$xuKI5`;U89^bWVbtdy z(59!QNzOkt$qhQaYI|o2>I>^SgSDeo1ruX+p~2cgxnO2^raM?v(N!>5TpDPb zC}hq?u^EY#OEER>D<8wQ7(TY6eX#v&7?VxKk@nk)#k6?sc%gw}~7d4?+w z1e^F_zHPeE+cnaocFz<}g?fD0l=|xjBw?!DSJEpNmimgO3(BWLk@D%T{#t)oU4=Sa zQZydyXbeuacNa`I6phTtJ&{gL@b@w~jSU~=iu!w!P_5V^H-&~P`kLX9ZIY899##>j)47umJP76G3&e;#taWi=r1C~4gVViZ+_rS4we?epakkCKc(oC{8XQo(e_E=y_)rZ1$rN?Tkd{s?K9oN_ruA5RPMXtK4o0}P~6{OnA zv4ZL@pD-9MFYg!)_cylojSp8&6jb$1477I!MyI;`>=@e>ah6;UJmDqu(Nw~Ra z#9J2BTYEzjQ|-mF8Y-7drpns-YeOM_e`H{yP8X|2dK(62`X{@ORo2!Fw5$CbKQqx( zTT@!q+*Z&VDr_C|Ro72dS4zI-GEr#`cLaQa!J4Xy{^rW+hW?78nI?I#I;?h9YkF5z zXFWIW?`bP59GGVFG1Sw1G!Ut5hg&<`(p%O(94P6T2-mikmNtylS5363;!H!EZ=$Bu z-yRAVmN@5wRR`mh!wT|h0(aucPwlKeSzje0Fyo7(#7$6I=aefp3%*xFJjmko7=%Erq_^@)zo zUT!+lJ>6M0R@zZiFygP7DJ`rnpKLAUrUy#8ht#0IS1#bIeYFK8eU;K^Wr0>8_4d{b zRKkvE;{{?rvm;@y@@QjY=k&<1I-u$WMHQ7ZVZE_J8t9U0XS&q3v9a+gUuC^~tfQi{ zpfv215fm0`D;(C4h>%1*8s59J`ovIyB2ET+g57FllAq`aOopWaeY7v=Q%1PRNJt2h zA(RoE;j!VNhRWuOiu&db@3DbkO|P%4aCEe+bfmp?e59jHE@-Y)s;Zj9)zjQeO>qJks2CG2YM!v!0!-8TB};Bl!l&}rpB3uV&6zu zC=r`$gY6w7{$rDlArtZn1>TT*ftxOg(dYMI#3coi92^Yw_WJvHJs9rlp6b>7(vTR? z!`*@Dpd9t(G4mQ_oa+BU)H7u0O4Axb4 z27`TkZ^cwqd)=|#NQjq5d;QMQDFiO1%A*{LRS41NceBxH>SXT-?;9N)93AY^r})s! zsM6Ktn~?$&q28{5kal!qqXk@bQ;D#7KK~J;>uG9;_}S=!lSkW{dqxXFGd(_kU2(X% z`B-OFd#^HCJ=0SZ84q^qQ<0g1p~}h$Uu(zMrqQ7mBr5YE(JHGaAK1mVJ-uI^5Qjp; zGrco?{bQjCU78&4>rwRXu?gNcG#Fw_dSJu0*ESg8hLnzCEmU9GH(e(CONPoC!p*%U zzL{=*xV_jnGuAlOIiq=_#g)D6!}sO1HH?USRW zk;3tDeoU?`Z*A2>T{7R=Rx~;oX{f3lX=`rj=qoNCu4p;d+S(l{D;*l|Yx0HpvJt+k zrlM%LtGr6Jf0S29k3k;gm(&9>=2=vH~p4$p?0_IT_N2yJmuZJ?QIGBWi@> z$A(6f;MAa~bkB@wlSx;^*!JLx)HGKOOlggi>M^;txwNgkq_iens|__5R<*VGYUKbo zCJ)sNcMYnYgXBj`L*wNEZLCmj3pNdO2P(Cx{vv-xV`oE!H`p`US=>`UskJqY4~%y8 zNh4ChwC*43RVpf5TKZe-yDCcS3PVHnRXr8H;?BV-t$2iQDI1a|N_!@|u^P3}>efN2 ztVGZz>c_+NP2PTg&FE;Qzg-*k)pj=@8wtpxBh8&n(^^|Y=-6a^)v&j%y*tRuYIU%@ zrnX^NuW9US@pt(8`3^DE!H3kA(VG6o(wV_ygC+cA?=WB6-!2S?>PyRp8nN}&$#vuK z7(450)b5_j(ZZfE+xp-y9j%+H;{9XI4N6yO?R1p}ZUcHnuqn_m(bF~1plH*5e7HiW z500S1tK}ZDIT7Wf0)JQ$RNU=2A@0D|H(n4CxzJ2+&(zF_(kl;-=~`%56bIyx)HU1} zk*3GkI_a-sZgh1+oi^y7YAI~*@2;Kls(RsgL+`L&*jnFSKH1VEioIh^MgFl`wY$^L zPtWxN}97{;Z`KVBQ^=`S4MN1K9EQ=PhRy0o(@s0~kz)yd6DzZMz~ z_KUu$CTWQCck2EU?O4HdXK|!gZYv4T474}|{t^+O!RpfTvPg9s*IZItSHh2U zO?S11LZ#DcP1&(l(cTQ1)KF;_PPK%@KS^yHlbSLwFE0Fb;NW+X6 z^c9T5oidLihSYc}A`Bn{>fg}$|KZHfek%k% zrpOWVFk(d?vpQH*SrZAF%cm&Z@%sURx`XYu< z?F-U#RQt+IVj!Ua9-tdOe#O3sZ`AvOAnkfzVEbcI?u)oX#V>GYSNzIMf>#ldN5r2( z0U&Ie;eqoj!HNO}`Kj$UCXke)Ng@HM$e$l`TDDua?=oj&ix?iR}FzS9~Cb8Gf z8eQ-;kj(t4mHi^dQ27gt*_FRClQ8m+SK%+>6}7*t9l)sl1!jLPN`DdasQv{(+tt4^ zlXn74uLdxfpJDMYViEPfz@lCMi ^J7&0i?{?lmzjbjJQr+!?9sWzH~_j}+yT%f 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 literal 0 HcmV?d00001 diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..9127d0e --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'A_core.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/media/profile_photos/Bongsu.jpg b/media/profile_photos/Bongsu.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d646adf7ac186017d1337f356ac02f084cc3fbe2 GIT binary patch literal 188323 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