diff --git a/tasks/concepts/a-campus-wayfinding.html b/tasks/concepts/a-campus-wayfinding.html new file mode 100644 index 0000000..0e93395 --- /dev/null +++ b/tasks/concepts/a-campus-wayfinding.html @@ -0,0 +1,38 @@ + + + + + + + مسیرهای انجمن — طرح A + + + +
نشان فعلی انجمنانجمن علمی مهندسی کامپیوتردانشکده‌ی فنی و مهندسی شرق گیلان
+
+

جایی برای یادگیری و همکاری دانشجویان کامپیوتر

انجمن علمی مهندسی کامپیوتر دانشگاه گیلان در دانشکده‌ی فنی و مهندسی شرق گیلان فعالیت می‌کند. اینجا می‌توانید با فعالیت‌های انجمن آشنا شوید و راه مشارکت خود را پیدا کنید.

هویت انجمن

انجمن علمی مهندسی کامپیوتر دانشگاه گیلان

دانشکده‌ی فنی و مهندسی شرق گیلان
+

از کجا شروع کنم؟

آشنایی با انجمنمأموریت و راه‌های ارتباطیرویدادهابرنامه‌ها و فرصت‌های یادگیریبلاگنوشته‌ها و محتوای آموزشی
+

درباره انجمن

یادگیری پیوسته، اشتراک دانش و فرصت برابر برای مشارکت، محور فعالیت انجمن است.

نشان فعلی انجمن

مأموریت

توانمندسازی دانشجویان با مهارت‌های موردنیاز صنعت، ایجاد شبکه‌های حرفه‌ای و فراهم‌کردن مسیرهای یادگیری پروژه‌محور.

فعالیت‌ها

برگزاری رویدادها، کارگاه‌ها و نشست‌های تخصصی و انتشار محتوای آموزشی.

+
+ + diff --git a/tasks/concepts/b-association-journal.html b/tasks/concepts/b-association-journal.html new file mode 100644 index 0000000..fdf02a4 --- /dev/null +++ b/tasks/concepts/b-association-journal.html @@ -0,0 +1,32 @@ + + + + + + + دفتر انجمن — طرح B + + + +
نشان فعلی انجمنانجمن علمی مهندسی کامپیوتر
دانشکده‌ی فنی و مهندسی شرق گیلان
+
+

انجمن برای یاد گرفتن با هم

انجمن علمی مهندسی کامپیوتر دانشگاه گیلان

در دانشکده‌ی فنی و مهندسی شرق گیلان، فعالیت انجمن فرصتی برای یادگیری، اشتراک دانش و مشارکت دانشجویان است.

انجمن را بشناسید
+
برای آشنایی با فعالیت‌هابرنامه‌های انجمنمأموریت و ارتباط
+

فعالیت‌های انجمن

از رویدادهای تخصصی تا نوشته‌های آموزشی، برنامه‌ها برای یادگیری و ارتباط میان دانشجویان شکل می‌گیرند.

رویدادها

کارگاه‌ها، سمینارها و نشست‌های علمی.

محتوا

مقالات آموزشی و یادداشت‌های تخصصی.

همکاری

مشارکت دانشجویان و اشتراک تجربه.

+

مأموریت انجمن، توانمندسازی دانشجویان با مهارت‌های موردنیاز صنعت، ایجاد شبکه‌های حرفه‌ای و فراهم‌کردن مسیرهای یادگیری پروژه‌محور است.

تلگرام
t.me/guilancea
اینستاگرام
instagram.com/guilance.ir
+
+ + diff --git a/tasks/concepts/c-current-refinement.html b/tasks/concepts/c-current-refinement.html new file mode 100644 index 0000000..9c8fc36 --- /dev/null +++ b/tasks/concepts/c-current-refinement.html @@ -0,0 +1,52 @@ + + + + + + + پالایش طرح فعلی — خانه و درباره انجمن + + + +
انجمن علمی مهندسی کامپیوتردانشکده‌ی فنی و مهندسی شرق گیلان
+
+

انجمن علمی مهندسی کامپیوتر

انجمن دانشجویان کامپیوتر در دانشکده‌ی فنی و مهندسی شرق گیلان است؛ جایی برای یادگیری، برگزاری رویدادهای تخصصی و همکاری با یکدیگر.

+

در انجمن چه می‌گذرد؟

سه راه ساده برای شناختن فعالیت‌های انجمن و پیدا کردن نقطه‌ی شروع.

رویدادهای تخصصی

کارگاه‌ها، سمینارها و نشست‌هایی برای یادگیری مهارت‌های تازه.

دیدن رویدادها

محتوای آموزشی

مطالبی در حوزه‌های گوناگون علوم و مهندسی کامپیوتر.

خواندن بلاگ

همکاری دانشجویی

فرصتی برای مشارکت در فعالیت‌ها و ارتباط با دانشجویان علاقه‌مند.

راه‌های ارتباطی
+

دوست دارید همراه انجمن باشید؟

برای آشنایی با فعالیت‌ها از همین‌جا شروع کنید یا برنامه‌های پیش رو را ببینید.

+

درباره انجمن

انجمن علمی مهندسی کامپیوتر در دانشکده‌ی فنی و مهندسی شرق گیلان با برگزاری رویدادها و تولید محتوای آموزشی، فضایی برای یادگیری و همکاری دانشجویان فراهم می‌کند.

انجمن علمی مهندسی کامپیوتر دانشگاه گیلان (دانشکده‌ی فنی و مهندسی شرق گیلان)
+

فعالیت‌های انجمن

محور فعالیت‌ها، یادگیری، اشتراک دانش و ارتباط میان دانشجویان و متخصصان است.

  • برگزاری رویدادهاکارگاه‌ها، سمینارها و نشست‌های تخصصی
  • انتشار محتوامقاله‌ها و مطالب آموزشی در حوزه‌ی کامپیوتر
  • مشارکت دانشجوییهمکاری در برنامه‌ریزی و اجرای فعالیت‌های انجمن
+

پرسش‌های رایج

چطور در رویدادها شرکت کنم؟

رویداد موردنظرتان را انتخاب کنید و شرایط و مراحل ثبت‌نام همان رویداد را بخوانید.

چطور با انجمن همکاری کنم؟

برای پیشنهاد همکاری از راه‌های ارتباطی بالا پیام بدهید.

+
+ + + + + diff --git a/tasks/evidence/2026-09-24/concept-a-1440.png b/tasks/evidence/2026-09-24/concept-a-1440.png new file mode 100644 index 0000000..62e732c Binary files /dev/null and b/tasks/evidence/2026-09-24/concept-a-1440.png differ diff --git a/tasks/evidence/2026-09-24/concept-a-390.png b/tasks/evidence/2026-09-24/concept-a-390.png new file mode 100644 index 0000000..6266859 Binary files /dev/null and b/tasks/evidence/2026-09-24/concept-a-390.png differ diff --git a/tasks/evidence/2026-09-24/concept-b-1440.png b/tasks/evidence/2026-09-24/concept-b-1440.png new file mode 100644 index 0000000..5a801d8 Binary files /dev/null and b/tasks/evidence/2026-09-24/concept-b-1440.png differ diff --git a/tasks/evidence/2026-09-24/concept-b-390.png b/tasks/evidence/2026-09-24/concept-b-390.png new file mode 100644 index 0000000..e0c5f4b Binary files /dev/null and b/tasks/evidence/2026-09-24/concept-b-390.png differ diff --git a/tasks/evidence/2026-09-24/create-local-baseline-fixtures.py b/tasks/evidence/2026-09-24/create-local-baseline-fixtures.py new file mode 100644 index 0000000..9eb5f4d --- /dev/null +++ b/tasks/evidence/2026-09-24/create-local-baseline-fixtures.py @@ -0,0 +1,74 @@ +"""Create disposable Bundle 0 browser fixtures in the isolated Django test database. + +Run only with --settings=config.settings.test and BUNDLE0_TEST_PASSWORD set. +Never point TEST_DB_NAME at a shared or production database. +""" + +import os +from datetime import timedelta + +from django.conf import settings +from django.utils import timezone + +from apps.blog.models import Post +from apps.events.models import Event +from apps.users.models import User + + +assert os.environ.get("DJANGO_SETTINGS_MODULE") == "config.settings.test" +assert settings.DATABASES["default"]["ENGINE"] == "django.db.backends.sqlite3" +password = os.environ["BUNDLE0_TEST_PASSWORD"] +now = timezone.now() + + +def fixture_user(username, mobile, *, staff=False): + user, _ = User.objects.get_or_create( + username=username, + defaults={"mobile": mobile, "email": f"{username}@example.invalid"}, + ) + user.mobile = mobile + user.email = f"{username}@example.invalid" + user.is_email_verified = True + user.is_mobile_verified = True + user.is_staff = staff + user.is_superuser = staff + user.set_password(password) + user.save() + return user + + +student = fixture_user("bundle0_student", "09198000111") +staff = fixture_user("bundle0_staff", "09198000112", staff=True) + +for slug, title, price, capacity in ( + ("bundle0-free", "Bundle 0 Free Event", 0, 20), + ("bundle0-full", "Bundle 0 Full Event", 0, 0), + ("bundle0-paid", "Bundle 0 Paid Event", 50000, 20), +): + Event.objects.update_or_create( + slug=slug, + defaults={ + "title": title, + "description": "Local baseline event for browser review.", + "start_time": now + timedelta(days=7), + "end_time": now + timedelta(days=7, hours=2), + "registration_start_date": now - timedelta(days=1), + "registration_end_date": now + timedelta(days=6), + "status": Event.StatusChoices.PUBLISHED, + "price": price, + "capacity": capacity, + }, + ) + +Post.objects.update_or_create( + slug="bundle0-article", + defaults={ + "title": "Bundle 0 Article", + "content": "# Local baseline article\n\nA sample article for the browser review.", + "author": staff, + "status": Post.StatusChoices.PUBLISHED, + "published_at": now, + }, +) + +print("Created isolated student, staff, free/full/paid events, and article fixtures.") diff --git a/tasks/evidence/2026-09-24/replay-local-auth.py b/tasks/evidence/2026-09-24/replay-local-auth.py new file mode 100644 index 0000000..494d70b --- /dev/null +++ b/tasks/evidence/2026-09-24/replay-local-auth.py @@ -0,0 +1,95 @@ +"""Replay signup, OTP login, and password reset in the isolated test database. + +SMS and the short-lived OTP store are mocked. Run after the fixture seed with +--settings=config.settings.test and BUNDLE0_TEST_PASSWORD set. +""" + +import json +import os +from unittest.mock import patch + +from django.conf import settings +from django.test import Client, override_settings + +from apps.users.models import User + + +assert os.environ.get("DJANGO_SETTINGS_MODULE") == "config.settings.test" +assert settings.DATABASES["default"]["ENGINE"] == "django.db.backends.sqlite3" +password = os.environ["BUNDLE0_TEST_PASSWORD"] +mobile = "09198000113" +client = Client() + + +class TemporaryOtpStore: + def __init__(self): + self.values = {} + + def setex(self, key, _seconds, value): + self.values[key] = str(value).encode("utf-8") + + def get(self, key): + return self.values.get(key) + + def delete(self, key): + self.values.pop(key, None) + + +store = TemporaryOtpStore() + + +def post(path, payload): + return client.post(path, data=json.dumps(payload), content_type="application/json") + + +with ( + override_settings(ALLOWED_HOSTS=["testserver", "localhost"]), + patch("apps.users.services.auth._redis", return_value=store), + patch("apps.users.services.auth.send_critical_sms.delay"), +): + invalid = post("/api/auth/otp/send", {"mobile": "123", "mode": "register"}) + assert invalid.status_code == 400 + + sent = post("/api/auth/otp/send", {"mobile": mobile, "mode": "register"}) + assert sent.status_code == 200, sent.content + code = store.get(f"auth_otp:register:{mobile}").decode("utf-8") + wrong = "00000" if code != "00000" else "99999" + rejected = post("/api/auth/otp/verify-register", {"mobile": mobile, "code": wrong}) + assert rejected.status_code == 400 + verified = post("/api/auth/otp/verify-register", {"mobile": mobile, "code": code}) + assert verified.status_code == 200, verified.content + registered = post( + "/api/auth/register", + {"username": "bundle0_signup", "mobile": mobile, "code": code, "password": password}, + ) + assert registered.status_code == 201, registered.content + assert User.objects.get(username="bundle0_signup").is_mobile_verified + + duplicate = post("/api/auth/otp/send", {"mobile": mobile, "mode": "register"}) + assert duplicate.status_code == 400 + + sent = post("/api/auth/otp/send", {"mobile": mobile, "mode": "login"}) + assert sent.status_code == 200, sent.content + code = store.get(f"auth_otp:login:{mobile}").decode("utf-8") + login = post("/api/auth/login/otp", {"mobile": mobile, "code": code}) + assert login.status_code == 200 and login.json()["access_token"] + + sent = post("/api/auth/otp/send", {"mobile": mobile, "mode": "reset_password"}) + assert sent.status_code == 200, sent.content + code = store.get(f"auth_otp:reset_password:{mobile}").decode("utf-8") + new_password = password + "New" + rejected = post( + "/api/auth/reset-password", + {"mobile": mobile, "code": wrong if wrong != code else "88888", "new_password": new_password}, + ) + assert rejected.status_code == 400 + changed = post( + "/api/auth/reset-password", + {"mobile": mobile, "code": code, "new_password": new_password}, + ) + assert changed.status_code == 200, changed.content + old_login = post("/api/auth/login", {"identifier": mobile, "password": password}) + new_login = post("/api/auth/login", {"identifier": mobile, "password": new_password}) + assert old_login.status_code == 401 and new_login.status_code == 200 + +print("Auth replay: invalid OTP request and code rejected; signup, OTP login, and password reset succeeded; old password rejected.") diff --git a/tasks/evidence/2026-09-24/replay-local-payment.py b/tasks/evidence/2026-09-24/replay-local-payment.py new file mode 100644 index 0000000..cc027a5 --- /dev/null +++ b/tasks/evidence/2026-09-24/replay-local-payment.py @@ -0,0 +1,85 @@ +"""Replay paid-event outcomes in the isolated Bundle 0 Django test database. + +External gateway calls, notifications, and SMS are mocked. This script is baseline +evidence, not a production payment test or a browser gateway substitute. +""" + +import json +import os +from unittest.mock import Mock, patch + +from django.conf import settings +from django.test import Client, override_settings + +from apps.events.models import Event, Registration +from apps.payments.models import Payment +from apps.users.models import User +from core.authentication import create_jwt_token + + +assert os.environ.get("DJANGO_SETTINGS_MODULE") == "config.settings.test" +assert settings.DATABASES["default"]["ENGINE"] == "django.db.backends.sqlite3" + +student = User.objects.get(username="bundle0_student") +event = Event.objects.get(slug="bundle0-paid") +client = Client() +headers = {"HTTP_AUTHORIZATION": f"Bearer {create_jwt_token(student)}"} +body = json.dumps({"event_id": event.pk, "description": "Local mocked payment replay"}) + + +def create_payment(): + return client.post( + "/api/payments/create", + data=body, + content_type="application/json", + **headers, + ) + + +with ( + override_settings(ALLOWED_HOSTS=["testserver", "localhost"]), + patch("apps.payments.api.views.notify_user"), + patch("apps.payments.api.views.send_critical_sms.delay"), + patch("apps.payments.api.views.requests.post") as gateway, +): + gateway.side_effect = RuntimeError("simulated gateway outage") + failed_request = create_payment() + assert failed_request.status_code == 502, failed_request.content + assert not Payment.objects.filter(user=student, event=event).exists() + + gateway.side_effect = None + gateway.return_value = Mock() + gateway.return_value.json.return_value = { + "data": {"code": 100, "authority": "BUNDLE0-CANCEL"} + } + pending = create_payment() + assert pending.status_code == 200, pending.content + assert pending.json()["start_pay_url"] + canceled = client.get( + "/api/payments/callback", + {"Authority": "BUNDLE0-CANCEL", "Status": "NOK"}, + ) + assert canceled.status_code == 302 + assert "status=failed" in canceled.url + assert Payment.objects.get(authority="BUNDLE0-CANCEL").status == Payment.OrderStatusChoices.CANCELED + + gateway.return_value.json.return_value = { + "data": {"code": 100, "authority": "BUNDLE0-SUCCESS"} + } + pending = create_payment() + assert pending.status_code == 200, pending.content + gateway.return_value.json.return_value = { + "data": {"code": 100, "ref_id": "BUNDLE0-REF"} + } + paid = client.get( + "/api/payments/callback", + {"Authority": "BUNDLE0-SUCCESS", "Status": "OK"}, + ) + assert paid.status_code == 302 + assert "status=success" in paid.url + assert Payment.objects.get(authority="BUNDLE0-SUCCESS").status == Payment.OrderStatusChoices.PAID + assert Registration.objects.get(user=student, event=event).status == Registration.StatusChoices.CONFIRMED + duplicate = create_payment() + assert duplicate.status_code == 400 + +print("Paid replay: gateway outage 502; cancellation redirect; mocked success and confirmed registration; duplicate blocked.") diff --git a/tasks/todo.md b/tasks/todo.md index 91b19ed..26e8eb2 100644 --- a/tasks/todo.md +++ b/tasks/todo.md @@ -33,10 +33,10 @@ Checkpoint C was approved by the owner on 2026-09-27 after reviewing [the schema ## 2. Home/About and shared public shell (9–12 person-days) -- [ ] **T15 — Establish approved visual tokens and shell (M; deps: T05, T14).** Files: F global styles, fonts, navigation/footer components. Acceptance: approved typography/colors/layout, responsive header/footer, and CMS identity/navigation/contact fields match comps; RTL/keyboard/reduced-motion work. Verify: screenshot comparison at 320/390/768/1440px and keyboard pass. -- [ ] **T16 — Build CMS-driven Home editorial sections and domain previews (M; deps: T15).** Files: F Home view, section components, tests. Acceptance: hero/featured-section/CTA text comes from the current CMS record in the fixed owner-approved order; event and blog cards read existing domain APIs with truthful status, useful links, and sound empty/long layouts. Verify: an admin Save changes the public text on the next request; event/blog source changes update cards; visual review at all widths. -- [ ] **Checkpoint G (deps: T15–T16).** Home concept fidelity and real content signed off before About. -- [ ] **T17 — Build CMS-driven About sections (M; deps: T15).** Files: F About view, section components, tests. Acceptance: official identity, mission, committee/association facts, and contacts come from reviewed current CMS records; unsupported claims are absent. Verify: compare rendered content to approved starting values and confirm an admin Save changes the page. +- [x] **T15 — Establish approved visual tokens and shell (M; deps: T05, T14).** Files: F global styles, fonts, navigation/footer components. Acceptance: approved typography/colors/layout, responsive header/footer, and CMS identity/navigation/contact fields match comps; RTL/keyboard/reduced-motion work. Implementation reviewed and approved by the owner at Checkpoint G on 2026-09-28. +- [x] **T16 — Build CMS-driven Home editorial sections and domain previews (M; deps: T15).** Files: F Home view, section components, tests. Acceptance: hero/featured-section/CTA text comes from the current CMS record in the fixed owner-approved order; event and blog cards read existing domain APIs with truthful status, useful links, and sound empty/long layouts. Home was reviewed and approved by the owner at Checkpoint G on 2026-09-28; frontend lint and production build passed. +- [x] **Checkpoint G (deps: T15–T16).** Owner approved the Home design and content at target widths on 2026-09-28. T17 can proceed. +- [ ] **T17 — Build CMS-driven About sections (M; deps: T15).** Files: F About view, section components, tests. Acceptance: owner-approved About identity, purpose, member-year archive, selected event highlights, separate memories gallery, and general history timeline come from current CMS records; unsupported claims are absent. Implementation is ready in the frontend working tree; final content and admin-save review remain open. Verify: compare rendered content to approved starting values and confirm an admin Save changes the page. - [ ] **T18 — Make page SEO/structured data content-driven (M; deps: T16–T17).** Files: F route metadata/SEO utilities/tests. Acceptance: canonical, title, description, OG image, and structured-data identity are consistent with current CMS records and server-rendered HTML; no stale hardcoded names. Verify: inspect HTML/JSON-LD and link previews on staging after an admin Save. - [ ] **T19 — Finish Home/About interaction and accessibility pass (M; deps: T16–T18).** Files: F affected page/styles/tests. Acceptance: motion supports comprehension but does not block content, reduced-motion works, contrast/focus/read order and Persian line wraps pass. Verify: real browser desktop/mobile captures, keyboard, screen-reader spot check, and Impeccable critique against approved comp. - [ ] **Checkpoint H (deps: T17–T19).** Owner approves Home/About on staging; authorized staff can Save a visible change without developer help.