| اگر تا حالا توی کوئرا سوالی حل نکردی این ویدیو رو ببین! |
|:---:|
| %video.arvan_https://player.arvancloud.ir/index.html?config=https://qvideo.arvanvod.ir/Z7LYW3YQBA/7xQM9V4Vk9/origin_config.json% |
+ محدودیت زمان: ۱ ثانیه
+ محدودیت حافظه: ۲۵۶ مگابایت
----------
در این سوال به شما دو عدد صحیح مثل $a$ و $b$ داده میشود. از شما میخواهیم برنامهای بنویسید که مقدار $a$ و $b$ را دریافت کند و $a + b$ را چاپ کند.
# ورودی
در تنها سطر ورودی، دو عدد صحیح $a$ و $b$ که با یک فاصله از هم جدا شدهاند، داده میشود.
$$1 \leq a, b \leq 100$$
# خروجی
در تنها سطر خروجی، مقدار $a + b$ را چاپ کنید.
# مثالها
## ورودی نمونه ۱
```
3 5
```
## خروجی نمونه ۱
```
8
```
## ورودی نمونه ۲
```
1 1
```
## خروجی نمونه ۲
```
2
```
<details class="red">
<summary>
# **اشتباهات متداول**
</summary>
<details class="red">
<summary>
**چک کردن شرایط ورودی مسئله**
</summary>
نیازی نیست چک کنید شرایط گفته شده در ورودی برقرار است یا نه. توضیحات محدودیتها فقط برای آگاهی شما دربارهی تستها و محدودیتهای مسئله است و قطعاً در ورودیهای داده شده به برنامهی شما رعایت میشوند. پس نیازی نیست بنویسید:
```python
if 1 <= n <= 100:
# answer of problem
else:
# print('invalid input')
```
</details>
<details class="red">
<summary>
**ابتدا همهی ورودی را گرفتن و در نهایت همهی خروجی را چاپ کردن**
</summary>
شما میتوانید لابهلای دریافت ورودی، خروجی دهید. پس نیازی نیست ابتدا همهی ورودیها را دریافت کنید و در نهایت همهی خروجیها را چاپ کنید. مخصوصاً برای سوالاتی که باید به چندین سوال پاسخ دهید، میتوانید دو قسمت ورودی و خروجی را کاملاً مستقل در نظر بگیرید و مطمئن باشید تداخلی پیش نمیآید.
</details>
<details class="red">
<summary>
**چاپ کردن موارد اضافه برای دریافت ورودی**
</summary>
لطفاً از چاپ کردن موارد اضافه مثل `please enter a number` برای دریافت ورودی پرهیز کنید. برای مثال در زبان پایتون نباید بنویسید:
```python
input('please enter:')
```
</details>
<details class="red">
<summary>
**چند فایلی کد زدن**
</summary>
برای زبانهایی مثل جاوا نباید در بالای کد شما آدرس پکیج داده شود. برای مثال در بالای کد خود نباید بنویسید:
```java
package ir.quera.contest;
```
</details>
<details class="red">
<summary>
**استفاده از چند `Scanner` برای دریافت ورودی**
</summary>
در زبان جاوا، باید فقط یک شئ از جنس `Scanner` تعریف کنید و همهی ورودیها را با آن دریافت کنید.
</details>
<details class="red">
<summary>
**نحوهی دریافت ورودی و چاپ کردن خروجی**
</summary>
برای آشنایی بیشتر برای نحوهی دریافت ورودی و چاپ کردن خروجی این [لینک](https://quera.org/course/assignments/2693/problems/8774) را مطالعه کنید.
</details>
جمع دو عدد (آموزشی)
> یک سالِ **پرماجرا** و **پرفناوری** بعد...
**امیرعوس** *(AmirOsssss)* که زمانی از شرکتکنندگان خفن و کاربلد [**سری اول #المپیکفناوری پردیس**](https://quera.org/events/techolympics-0307) بود، حال پس از گذشت یک سال **پرماجرا** و **پرفناوری** و سفید شدن تعداد زیادی از موهایش، خود به **عضوی تاثیرگذار** و **ارزشمند** از کوئرا تبدیل شده و امسال عضوی از تیم طراحان و برگزارکنندگان [**سری دوم #المپیکفناوری پردیس**](https://quera.org/events/techolympics-0407) است. او که تجربهی بسیاری در شرکت و برگزاری ایونتهای خاص برنامهنویسی و هوشمصنوعی دارد، از سمت **باقر** *(Bagher)* به عنوان **مدیر تدارکات** این سری از رویداد کشوری المپیکفناوری منصوب شده است.
**زبان ایونتی** امیرعوس ترکیبی از **نمادها** و **نشانههایی** است که مشابه **الفبای انگلیسی عادی** هستند. او از یک ترتیب جدید به نام **کوئرانومریک** *(Queranumeric)* بهجای ترتیب [**دیکشنری** *(Alphanumeric)،*](https://en.wikipedia.org/wiki/Alphanumericals) برای **مرتبسازی اسامی شرکتکنندگان** استفاده میکند؛ سیستمی که در آن نمادها و کاراکترها، طبق یک **ترتیب خاص** از پیش تعیینشده، اولویت خواهند داشت. از آنجایی که امیرعوس امسال به دلیل برگزاری المپیکفناوری **به صورت بینالمللی** حسابی مشغول است، از شما میخواهد تا برنامهای بنویسید که اسامی تیمهای شرکتکننده مسابقات را به گونهای مرتب کند که ترتیب آنها بر اساس **جایگاه حروف در ترتیب کوئرانومریک** مشخص شود.

# **پروژهی اولیه**
برای دانلود **پروژهی اولیه** روی [این لینک](/contest/assignments/84123/download_problem_initial_project/305472/) کلیک کنید.
# **جزئیات پروژه**
در ابتدا **ترتیب کوئرانومریک** به صورت **یک رشته** به شما داده میشود که شامل **کاراکترهای خاص** و دلخواه است *(ممکن است حروف، اعداد یا نمادهای دیگر هم باشد).* سپس به ترتیب $n$ رشته، که **اسامی تیمهای شرکتکنندگان المپیکفناوری** هستند، ورودی داده میشود. شما باید این اسامی را به گونهای مرتب کنید که **ترتیب آنها بر اساس جایگاه حروف در ترتیب کوئرانومریک مشخص شود.**
اگر دو رشته در بخشی از مقایسه **مساوی** باشند *(مثلاً چند حرف اولشان یکسان باشد)،* از **کاراکتر بعدی ب**رای تصمیمگیری استفاده میشود. همچنین اگر یکی از اسامی پیش از تمامشدن مقایسه تمام شود *(یعنی کوتاهتر باشد)،* آن رشته **کوچکتر** در نظر گرفته میشود.
دو عدد **صحیح و مثبت** $n$ و $l$ به ترتیب **نمایانگر تعداد تیمها** و **بیشترین طول مجاز برای اسم یک تیم** میباشند و تضمین میشود در بازهی عددی زیر قرار دارند:
$$
0 \le n \le 4000
$$
$$
1 \le l \le 5000
$$
# **ورودی**
**توجه داشته باشید که این مسئله ورودی استاندارد ندارد.**
بهجای آن، تابع زیر را در **فایل** `solution.py` پیادهسازی کنید. این تابع ورودیها را بهصورت آرگومان توسط سیستم داوری، دریافت خواهد کرد.
```python solution.py python
def queranumeric(order: list[str], words: list[str]) -> list[str]:
return []
```
- **پارامتر** `order`**:** لیستی از **کاراکترهای بدون تکرار** است که **ترتیب کوئرانومریک** را مشخص میکند؛ **عنصر اول بااولویتترین کاراکتر** و به همین ترتیب تا انتها و عنصر انتهایی، **کمترین اولویت** را در مرتبسازی خواهد داشت.
- **پارامتر** `words`**:** **لیستی از رشتهها،** که همان اسامی تیمهای شرکتکننده هستند، که باید بر اساس **ترتیب کوئرانومریک** مرتب شوند.
**نکته:** اگر **کاراکتری** در `words` وجود داشته باشد که در `order` نیامده است، آن کاراکتر، **کماهمیتتر از همهی کاراکترهای موجود** در `order` در نظر گرفته خواهد شد.
# **خروجی**
تابع باید **لیستِ جدیدِ مرتبشده** از اساسی تیمهای شرکتکننده را مطابق **ترتیب کوئرانومریک** برگرداند *(*`return` *کند).*
# **مثال**
### **ورودی نمونه ۱**
```python solution.py python
order = list("cba")
words = ["a", "ba", "cc"]
queranumeric(order, words)
```
### **خروجی نمونه ۱**
```python solution.py python
["cc", "ba", "a"]
```
+ در این مثال، **ترتیب کوئرانومریک** `"cba"` است؛ یعنی اولویت کاراکترها بهترتیب `c`، بعد `b` و در نهایت `a` است. هنگام مرتبسازی، ابتدا **حرف اول هر رشته** را با هم مقایسه میکنیم: `"cc"` با `c` شروع میشود و چون `c` بالاترین رتبه را دارد، هر رشتهای که با `c` شروع شود **جلوتر** قرار میگیرد؛ پس `"cc"` اول میآید. بین دو رشتهی باقیمانده، `"ba"` با `b` شروع میشود که از `a` مهمتر است، بنابراین `"ba"` قبل از `"a"` میآید.
### **ورودی نمونه ۲**
```python solution.py python
order = list("namrepus")
words = ["sun", "man", "super", "name", "user", "spam", "ram"]
queranumeric(order, words)
```
### **خروجی نمونه ۲**
```python solution.py python
["name", "man", "ram", "user", "spam", "sun", "super"]
```
+ در اینجا **ترتیب کوئرانومریک** `"namrepus"` است؛ یعنی **اولویت** از `n` شروع و بهترتیب `a`، `m`، `r`، `e`، `p`، `u` و در نهایت `s` است. ابتدا `"name"` که با `n` شروع میشود در **ابتدای لیست مرتبشده** قرار میگیرد، چون **بالاترین اولویت** را دارد. سپس رشتههایی که با `a`، `m` و `r` شروع میشوند بهترتیب میآیند: `"man"` (شروع با `m`) قبل از `"ram"` (شروع با `r`) قرار میگیرد چون `m` در رتبهی بالاتری از `r` است. باقی رشتهها با `u` و `s` شروع میشوند که در **انتهای ترتیب** هستند؛ طبق اولویت، رشتههای شروعشده با `u` (`"user"`) قبل از رشتههای شروعشده با `s` (`"spam"`, `"sun"`, `"super"`) میآیند و در میان `s`-ها، مقایسه به حروف بعدی کشیده میشود که **ترتیب** `"spam"`, `"sun"`, `"super"` را رقم میزند.
# **آنچه باید آپلود کنید**
+ **توجه**: پس از پیادهسازی تابع خواسته شده، **فایل** `solution.py` را برای سیستم داوری ارسال کنید.
+ **توجه**: شما مجاز به افزودن فایل جدیدی در این ساختار **نیستید** و تنها باید تغییرات را در **فایل** `solution.py` اعمال کنید.
+ **توجه**: ایجاد هرگونه **تغییرات اضافی** در **امضا** *(Signature)* و **خروجی تابع** `queranumeric` که خارج از تعریف سوال باشد، در سیستم داوری **مورد پذیرش قرار نگرفته** و نمرهای دریافت **نخواهد** کرد.
+ **توجه:** **فایل** `solution.py` **نباید** هیچ **عملکرد اضافهای** برای گرفتن **ورودی استاندارد** *(stdin)* و دادن **خروجی استاندارد** *(stdout)* مانند `print` کردن پاسخ را شامل باشد، در غیر این صورت نمرهای دریافت **نخواهد** کرد. سیستم داوری خود مسئول **فراخوانی تابع** `queranumeric`، دادن آرگومانهای ورودی به آن و بررسی خروجی میباشد.
کوئرانومریک
> **کانکشنیجات** *(Connectionijat)* جزو مهمی از برگزاری ایونتهای کشوری مانند **المپیکفناوری پردیس** هستند که **اختلالات** یا **شنود** در آنها میتواند منجر به تبدیل شدن به یک **فاجعه واقعی** خواهد شد...
**آقای لطفی** *(Mr.Lotfi)* **اسکواد لید کانتست** کوئرا که همواره پس از برگزاری **مسابقات** و **هکاتونها** توسط کوئرا، **نظرسنجیهای مفصلی** را برای سنجیدن و بهبود هر چه بیشتر تمام جنبههای رویداد برگزار شده از شرکتکنندگان انجام میدهد، پس از دریافت نتایج نظرسنجی مسابقات برنامهنویسی و هوشمصنوعی سری اول #المپیکفناوری پردیس، به یک مشکل اساسی که کاربران زیادی به آن پرداخته بودند پی میبرد، مشکلات کانکشنیجات!
کاربران در نظرسنجیها به **نبود زیرساختی مطمئن** و **سریع** برای **برقراری کانکشنیجات** بین خودشان و طراحان و دستاندرکاران **سری رویدادهای المپیکفناوری پردیس** معترض بودند! از آنجایی که **آقای لطفی** مثل همیشه مشتاق جلب بیشتر رضایت شرکتکنندگان است، به **پیمانوش** *(Peymanoosh)* در **تیم فنی کوئرا،** ماموریت جدیدی داده تا برنامهای برای برقراری ارتباطات بهتر میان شرکتکنندگان و برگزارکنندگان سری جدید #المپیکفناوری پردیس با عنوان "فناوری کانکشنیجات" توسعه دهد.

*فناوری کانکشنیجات* که بر اساس ارسال **بستههای داده** *(Data Packet)* طوری طراحی شده است که برای جلوگیری از شنود ارتباطات بین طراحان و برگزارکنندگان توسط شرکتکنندگان عادی، بستههای **طعمه** *(Bait)* ایجاد کند که اطلاعات واقعی ندارند، از دو بخش **ارسالکننده پیام** *(Transmitter)* و **دریافتکننده پیام** *(Receiver)* تشکیل شده است. همچنین علی رغم دانش فنی فراوان پیامنوش، به دلیل وسعت بسیار زیاد پارک فناوری پردیس، بستهها ممکن است با [**ترتیب نامشخص** *(Jitter)*](https://en.wikipedia.org/wiki/Jitter) به مقصد برسند.
# **پروژهی اولیه**
برای دانلود **پروژهی اولیه** روی [این لینک](/contest/assignments/84123/download_problem_initial_project/305473/) کلیک کنید.
<details class="yellow">
<summary>**ساختار فایلها**</summary>
```
fanavari-connectionijat/
├── tests/
├ ├── __init__.py
├ └── test_sample.py
├── base.py
├── <mark class="orange" title="این فایل را تکمیل کنید">packet.py</mark>
├── <mark class="orange" title="این فایل را تکمیل کنید">receiver.py</mark>
└── <mark class="orange" title="این فایل را تکمیل کنید">transmitter.py</mark>
```
</details>
# **جزئیات پروژه**
**تیم فنی کوئرا** و به ویژه **پیمانوش** این روزها به دلیل المپیکفناوری، لود زیادی را تحمل میکنند؛ از این رو پیمانوش تنها **بعضی بخشهای فناوری کانکشنیجات** را توسعه داده و پیادهسازی باقی قسمتهای آن را بر عهده شما قرار داده است تا شما نیز در توسعه بخشی از زیرساخت ارتباطاتی المپیکفناوری سهیم باشید. شما در این سوال قرار است بخشهای زیر از **فناوری کانکشنیجات** را طراحی کنید:
1. یک [**دیتاکلاس (Data Class)**](https://docs.python.org/3/library/dataclasses.html) برای مدلسازی **بستهها** پیادهسازی کنید.
2. کلاسی تحت عنوان **فرستنده** پیادهسازی کنید تا پیامها را با فرمت بستههای مدلسازی شده ارسال کند؛ همچنین متدی برای اضافه کردن **بستههای تقلبی** اضافه کنید تا در مرحلهی تست پیمانوش از آن استفاده شود.
3. کلاسی تحت عنوان **گیرنده** پیادهسازی کنید تا با دریافت پیام، بستههای طعمه را **فیلتر** کند و با مرتبسازی **بستههای واقعی** بر اساس شماره توالی، **محتوای پیام اولیه** را بازسازی نمایید.
<details class="red">
<summary>**پیادهسازی فایل** `base.py`</summary>
این فایل شامل **کلاس پایهای** به نام `Base` است که یک تابع کمکی مهم برای انجام عملیات هش *(Hash)* در اختیار **سایر کلاسها** (`Transmitter` و `Receiver`) قرار میدهد.
محتوای این فایل از قبل به شکل زیر پیادهسازی شده است و در **فایل پروژه اولیه** قرار گرفته است:
```python base.py python
import hashlib
class Base:
def _hash_function(self, data: str, key: str) -> str:
return hashlib.sha256((data + key).encode()).hexdigest()
```
</details>
<details class="blue">
<summary>**پیادهسازی فایل** `packet.py`</summary>
در این فایل باید **دیتاکلاس** `Packet` را پیادهسازی کنید. هر بستهی دادهای که بین **فرستنده** و **گیرنده** در **فناوری کانکشنیجات** منتقل میشود، با این ساختار مدلسازی شده است که فیلدهای آن به شکل زیر میباشد:
| نام فیلد | نوع داده | توضیح |
| ----------------- | -------- | ------------------------------- |
| `sequence_number` | `int` | مشخصکنندهی جایگاه بسته در پیام اصلی (برای مرتبسازی) |
| `data` | `str` | محتوای واقعی بسته (یک بخش از پیام اصلی یا طعمه) |
| `hashed_data` | `str` | هش تولید شده از `data` با استفاده از کلید عمومی و تابع موجود در کلاس `Base` |
+ **توجه کنید** که شما باید **فیلدهای جدول بالا** را **دقیقا** با **نام و نوع** گفته شده پیادهسازی کنید، در غیر این صورت در سیستم داوری نمرهای به شما تعلق **نخواهد گرفت.**
+ همچنین **توجه داشته** باشید که شما **مجازید** تا **هر تعداد فیلد دلخواه دیگری** را نیز برای پیادهسازی ادامه برنامه، در این کلاس معرفی کنید.
## **پیادهسازی متد** `is_valid`
```python packet.py python
def is_valid(self, hash_func: Callable, key: str) -> bool:
pass
```
+ **خروجی** این متد از نوع **بولین** خواهد بود؛ در صورت برابر بودن **مقدار** `hashed_data` بستهی موردنظر با مقدار **خروجی تابع** `hash_func` خروجی برابر با `True` و در غیر اینصورت برابر با `False` خواهد بود.
**نکته:** **تابعی** که در **پارامتر** `hash_func` به متد موردنظر پاس داده میشود، دارای **دو ورودی از نوع رشته** خواهد بود که **ورودی اول رشته** `data` بستهی موردنظر و **ورودی دوم** `key` میباشد که **کلیدی** است که با آن `data` **هش** شده است.
نمونهای از این تابع:
```python
def example_hash_func(data: str, key: str) -> str:
return f"hashed_{key}"
```
+ **توجه داشته باشید** که عملکرد هش کردن این تابع در تستهای سوال **متغیر** خواهد بود و همیشه عملیات هش کردن به شکل بالا انجام **نخواهد شد. این مورد صرفا یک نمونه برای درک بهتر سوال است.**
</details>
<details class="green">
<summary>**پیادهسازی فایل** `transmitter.py`</summary>
در این فایل باید **کلاس** `Transmitter` را پیادهسازی کنید. این کلاس **پیام ورودی** را به تکههای کوچک تقسیم میکند، برای هر تکه یک **بستهی واقعی** با هش معتبر میسازد، سپس به **تعداد مشخصی** بستهی **طعمه** اضافه میکند، **ترتیب** همهی بستهها را **بههم میزند** و آنها را به گیرنده ارسال میکند. این کلاس از `Base` **ارثبری** میکند و برای **ساخت هش** از متد کمکی داخل این کلاس استفاده میکند.
## **پیادهسازی سازندهی کلاس**
در **متد سازندهی کلاس** `Transmitter`، کلید مشترک که برای تولید هش بستههای واقعی استفاده میشود به عنوان ورودی دریافت شده و در **متغیر داخلی** `self._key` ذخیره میگردد. علاوه بر این، هنگام ایجاد شیء میتوان پارامترهای دیگری نیز مشخص کرد. **پارامتر** `self.bait_count` **تعداد بستههای طعمهای** را تعیین میکند که در ادامه ساخته خواهند شد، همچنین **پارامتر** `self.chunk_size` اندازهی هر برش از پیام را مشخص میکند تا پیام اصلی در **فرآیند تکهتکهسازی** به قطعات مناسب تقسیم شود. این مقادیر علاوه بر کلید ورودی، امکان مدیریت دقیقتر و انعطافپذیری بیشتر در پردازش و اعتبارسنجی دادهها را فراهم میآورند.
```python transmitter.py python
def __init__(key: str, bait_count: int = 2, chunk_size: int = 3):
pass
```
## **پیادهسازی متد** `_add_bait_packets`
این متد به **تعداد** `bait_count` **بستههای طعمه** میسازد و آنها را به **لیست** `packets` ارسال شده اضافه میکند. **توجه داشته باشید که محتوای بستههای طعمه اهمیتی ندارد.**
```python
def _add_bait_packets(self, packets: list) -> None:
return None
```
## **پیادهسازی متد** `_prepare_packets`
این متد ابتدا پیام را به **تکههایی** با **طول** `chunk_size` تقسیم میکند (تکهی آخر ممکن است از این مقدار کمتر باشد): سپس برای هر تکه، هش معتبر را با توابع موجود تولید میکند و یک نمونه از دیتاکلاس `Packet` با دادههای فعلی ایجاد میکند که شمارهی هر بسته نیز به **صورت متوالی** با شروع از عدد `1` شمارهگذاری میشود.
سپس با استفاده از متد `_add_bait_packets`، طعمهها را به لیست بستهها اضافه میکند و در پایان لیست نهایی را بُر میزند *(Shuffle)* و آن را برمیگرداند *(*`return` *میکند).*
```python transmitter.py python
def _prepare_packets(self, message: str) -> list[Packet]:
pass
```
## **پیادهسازی متد** `transmit`
این **متد** باید پیام موردنظر را به گیرندهی مربوطه ارسال میکند؛ برای اینکار ابتدا با **متد** `_prepare_packets(message)` بستهها ساخته شود و سپس آنها را به **متد** `receive` گیرنده ارسال کند؛ درنهایت همان بستهها را به عنوان خروجی متد برگردانده شود.
```python transmitter.py python
def transmit(self, message: str, receiver: Receiver) -> list[Packet]:
pass
```
</details>
<details class="yellow">
<summary>**پیادهسازی فایل** `receiver.py`</summary>
در این **فایل،** شما باید **کلاسی** به نام `Receiver` پیادهسازی کنید که بتواند بستههای دریافتی از فرستنده را بررسی کرده؛ **بستههای تقلبی** *(Bait)* را شناسایی و فیلتر کند و در نهایت پیام اصلی را بازسازی کند. **این کلاس دارای سه متد زیر است که باید هر یک را به صورت خواسته شده پیادهسازی کنید.**
## **پیادهسازی سازندهی کلاس**
این **متد** هنگام ساخته شدن شیء از **کلاس** `Receiver` فراخوانی میشود و **کلید عمومی** (رشتهی `pub_key`) را به عنوان ورودی دریافت میکند و در پارامتر `self._key` قرار میدهد. از این کلید جلوتر در اعتبارسنجی هشها استفاده خواهد شد. **توجه داشته باشید که شما میتوانید متغیرهای کمکی دیگری نیز در متد ایجاد کنید.**
```python receiver.py python
def __init__(self, key: str):
pass
```
## **پیادهسازی متد** `receive`
این **متد،** یک لیست از بستهها (`packets`) که هر عضو آن از نوع `Packet` است را دریافت میکند. باید مشخص شود که هر بسته معتبر است یا خیر و در صورت معتبر بودن، در یک لیست دیگر ذخیره شود. برای بررسی **معتبر بودن** هر بسته با استفاده از **متد**`is_valid` بستهی موردنظر استفاده شود. فقط بستههایی که **فیلد** `hased_data` آنها با مقدار **هش** `data` با استفاده از کلید برابر است را ذخیره کند و **بستههای نامعتبر** نادیده گرفته شوند.
```python receiver.py python
def receive(self, packets: list[Packet]) -> None:
pass
```
## **پیادهسازی متد** `get_message`
این متد، پس از با فراخوانی **متد قبلی** *(دریافت تمام بستهها و حذف بستههای طعمه)* باید با **مرتبسازی لیست بستهها،** رشتهی حاوی پیام اصلی را بازسازی کند. در صورتی که دو بستهی معتبر با شمارهی توالی برابر وجود داشته باشند باید **به ترتیب ورودی در پیام خروجی نمایش داده شوند.**
```python receiver.py python
def get_message(self) -> str:
pass
```
</details>
## **مثالها**
### **نمونهی مثال ۱**
```python main.py python
from transmitter import Transmitter
from receiver import Receiver
transmitter = Transmitter(key="k", bait_count=2, chunk_size=3)
receiver = Receiver("k")
packets = transmitter.transmit("hello world", receiver)
print(packets)
# ["hel", "low", "orl", "d"]
message = receiver.get_message()
print(message)
# hello world
```
+ در این مثال، پیام به **تکههای** `["hel", "low", "orl", "d"]` تقسیم میشود و سپس **سه بستهی طعمه** به لیست بستهها افزوده و کل لیست درهمریزی میشود. سپس با استفاده از **متد** `transmit` به گیرندهی مربوطه ارسال میشود؛ در نهایت با **فراخوانی متد** `get_message` در گیرندهی موردنظر، **پیام اولیه** (`hello world`) چاپ میشود.
### **نمونهی مثال ۲**
```python main.py python
from transmitter import Transmitter
from receiver import Receiver
transmitter = Transmitter(key="s3cr3t", bait_count=3, chunk_size=4)
receiver = Receiver("s3cr3t")
packets = transmitter.transmit("abcdefghijk", receiver)
# ["abcd", "efgh", "ijk"]
message = receiver.get_message()
# abcdefghijk
```
* در این مثال، پیام به **تکههای** `["abcd", "efgh", "ijk"]` تقسیم میشود، سپس **سه بستهی طعمه** به لیست بستهها افزوده و کل لیست درهمریزی میشود. سپس با استفاده از **متد** `transmit` به گیرندهی مربوطه ارسال میگردد؛ در نهایت با **فراخوانی متد** `get_message` در گیرنده، **پیام اولیه** (`abcdefghijk`) بازسازی و چاپ میشود.
# **آنچه باید آپلود کنید**
+ **توجه**: پس از اعمال تغییرات، کل پروژه را _Zip_ کرده و آپلود کنید. **همانند پروژه اولیه در فایل زیپ شده نباید کد در پوشهی دیگری قرار بگیرد در غیر این صورت سیستم داوری فایل را شناسایی نکرده و نمرهای دریافت نخواهید کرد.**
+ **توجه:** تنها فایلهایی که در **ساختار پروژه** مشخص شدهاند، در سیستم داوری **مورد پذیرش** قرار خواهد گرفت و سایر تغییرات در سایر فایلها **بیتاثیر** خواهند بود.
فناوری کانکشنیجات
> **برنامهنویسها** همواره **تعاریف متفاوتی** از خیلی از جنبههای زندگی ارائه میکنند، یکی از این **تعاریف جدید،** معنای متفاوت [**آبگوشت** *(Abgoosht)*](https://fa.wikipedia.org/wiki/%D8%A2%D8%A8%DA%AF%D9%88%D8%B4%D8%AA) است! آنها به کدهایی که **سهوا** یا **عمدا** مبهم شده باشند، **آبگوشت** میگویند. با این تعریف، *آبگوشتینگ* به عملیات [**مبهمسازی** *(Obfuscation)*](https://en.wikipedia.org/wiki/Obfuscation_%28software%29) و *دیآبگوشتینگ* به عملیات **رفع ابهام** *(DeObfuscation)* گفته میشود.
**دیواین** *(Diwin)* که همچنان هنوز هم مثل قبل بر پیادهسازی **ایدههای عجیب** خود استوار است، اینبار در سری جدید [**مسابقات _#المپیکفناوری_ پردیس**](https://quera.org/events/techolympics-0407)، تصمیم گرفته تا **سطح امنیت مسابقات** امسال را به شدت و **به طرز ابلفضلیای** افزایش دهد تا جلوی تمام **تقلبها** و **نشت کدها** را در میانهی مسابقات بگیرد. او اما از روشی بسیار عجیبی برای این کار استفاده خواهد کرد، **درست کردن آبگوشت** از کدهای ارسال شده توسط شرکتکنندگان!
دیواین برای **آبگوشتینگ** *(Obfuscation)* کدهای شرکتکنندگان از **پارسر** *(Parser)* قدرتمندی به نام `abgoosht_parser` که از محصولات تولید داخل، توسط **المپیکفناوریون** *(Olampici Fanavarion)* برتر پارسال است، استفاده خواهد کرد اما از آنجایی که **دیواین** در این سری از **مسابقات المپیک فناوری،** برای حفاظت بیشتر مسابقات باید **بیشتر از همیشه** حواسش به همه باشد، تنها کدهای **پارسر** `abgoosht_parser` را در اختیار شما قرار داده است و شما وظیفه دارید تا تکنیکهای **آبگوشتینگ** مد نظر او را در این سوال پیادهسازی کنید.

# **پروژه اولیه**
برای دانلود **پروژهی اولیه** روی [این لینک](/contest/assignments/84123/download_problem_initial_project/305474/) کلیک کنید.
<details class="yellow">
<summary>
**ساختار فایلها**
</summary>
```
abgoosht-fanavari
├── <mark class="blue" title="طبق توضیحات قسمت راهاندازی پروژه این داکرفایل را بیلد کنید">Dockerfile</mark>
├── <mark class="green" title="این پوشه از قبل پیادهسازی شده است">abgoosht_parser</mark>
├── benchmarks
├── <mark class="green" title="این فایل از قبل پیادهسازی شده است">gradio_app.py</mark>
├── <mark class="green" title="این فایل از قبل پیادهسازی شده است">main.py</mark>
├── obfuscator
│ ├── <mark class="green" title="این فایل از قبل پیادهسازی شده است">parser.py</mark>
│ ├── <mark class="green" title="این فایل از قبل پیادهسازی شده است">generator.py</mark>
│ ├── techniques
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">alias_generator.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">dead_code.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">expr_complexifier.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">opaque_predicate.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">function_splitter.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">misleading_comments.py</mark>
│ │ ├── <mark class="orange" title="این فایل باید پیاده سازی شود">flow_flattener.py</mark>
│ │ └── <mark class="orange" title="این فایل باید پیاده سازی شود">rename_vars.py</mark>
│ └── <mark class="green" title="این فایل از قبل پیادهسازی شده است">transformer.py</mark>
└── requirements.txt
```
</details>
<details class="grey">
<summary>
**راهاندازی پروژه**
</summary>
برای اجرای پروژه، باید **داکر، پایتون و ابزار** *pip* را از قبل نصب کرده باشید.
- ابتدا **پروژهی اولیه** را دانلود و از حالت فشرده خارج کنید.
- در پوشهی اصلی پروژه، یک **محیط مجازی پایتون** *(venv)* ایجاد و فعال کنید:
```bash terminal terminal
python -m venv venv
source venv/bin/activate # در ویندوز: venv\Scripts\activate
```
- دستور زیر را برای **نصب نیازمندیها** در پوشهی اصلی پروژه اجرا کنید:
```bash terminal terminal
pip install -r requirements.txt
```
- برای **اجرای رابط کاربری پروژه** که از قبل با استفاده از [*Gradio*](https://www.gradio.app/) طراحی شده است، دستور زیر را در مسیر پوشهی اصلی پروژه اجرا کنید:
```bash terminal terminal
python gradio_app.py
```
در صورت اجرای موفق، **یک لینک** در خروجی نمایش داده میشود که میتوانید **آن را در مرورگر باز کنید.**
- برای اجرای تستهای نمونهی پروژه، میتوانید از دستور زیر استفاده کنید:
```bash terminal terminal
python -m unittest discover tests
```
## **اجرای پروژه با داکر** *(Docker)*
در صورتی که **ابزار داکر** روی سیستم شما نصب است، میتوانید **پروژه** را به کمک داکرفایل آمادهی `Dockerfile` اجرا کنید:
- ابتدا **پروژه اولیه** را دانلود و از حالت فشرده خارج کنید.
- در پوشهی اصلی پروژه، دستور زیر را برای **ساخت ایمیج از روی داکرفایل** اجرا کنید:
```bash docker docker
docker build -t abgoosht-app .
```
- سپس برای اجرای پروژه، دستور زیر را وارد کنید:
```bash docker docker
docker run -p 7860:80 \
-v $(pwd)/examples:/app/examples \
-v $(pwd)/benchmarks:/app/benchmarks \
abgoosht-app
```
در صورت اجرای موفق، **رابط کاربری پروژه** از طریق آدرس http://localhost:7860 قابل دسترسی خواهد بود.
- برای **اجرای تستها** در محیط داکر، دستور زیر را اجرا کنید:
```bash docker docker
docker exec -it abgoosht-app python -m unittest discover tests
```
</details>
# **جزئیات پروژه**
در این پروژه، شما قرار است مجموعهای از **تکنیکهای مبهمسازی کدهای** *MiniC* را روی [**ساختار نحوی انتزاعی** *(AST)*](https://en.wikipedia.org/wiki/Abstract_syntax_tree) اعمال کنید. هدف اصلی، پیادهسازی ابزاری است که بتوانند **کدهای** *MiniC* را به صورت ایستا تحلیل کرده و تغییراتی مانند *افزودن کامنتهای بیمعنا، بازنویسی عبارات منطقی، افزودن انتسابهای زائد و بازنویسی جریان کنترل* را روی آن اعمال کنند. برای این کار، از *AST* تولیدشده توسط **پارسر** `abgoosht_parser` استفاده میکنید و با بهرهگیری از [**دیزاین پترن ویزیتور** *(Visitor)*](https://refactoring.guru/design-patterns/visitor)، **گرههای مختلف درخت** را پیمایش و تکنیکهای خواسته شده را روی **کدهای ورودی** اعمال میکنید.
<details class="blue">
<summary>
**معرفی زبان برنامهنویسی** *MiniC* **- همان** *C* **ولی کوچولوش!**
</summary>
**زبان برنامه نویسی** *MiniC* در واقع همان **زبان برنامه نویسی** *C* است، اما نسخهای **سادهتر** و **سبکشده** از آن محسوب میشود. این زبان **ساختار** و **قواعد** اصلی *C* را حفظ کرده اما برخی امکانات پیچیدهتر مانند **هدر فایلها** (`#include`)، **ساختارها** (`struct`) و **اشارهگرها** *(Pointers)* را ندارد. هدف از طراحی *MiniC* **سادهسازی زبان** و تمرکز بر **مفاهیم پایهای برنامهنویسی** است تا یادگیری و همچنین پیادهسازی **مفسر** یا **کامپایلر** برای آن راحتتر باشد.
در *MiniC* تنها **عناصر اصلی یک زبان برنامهنویسی** مانند **انواع دادهٔ ابتدایی** (`int`، `float`، `char`)، عملگرهای ریاضی و منطقی، **دستورات شرطی** (`if`، `else`) و **حلقهها** (`while`، `for`) پشتیبانی میشوند. برای ورودی و خروجی نیز به جای توابع کتابخانهای، معمولاً از دستورهای سادهتری مانند `print` یا `read` استفاده میشود. همین موضوع باعث میشود برنامهها **کوتاهتر** و **شفافتر** نوشته شوند و **پیچیدگیهای غیرضروری حذف گردند.**
کاربرد اصلی *MiniC* بیشتر در حوزههای **آموزشی** و **تحقیقاتی** مانند درسهای **طراحی زبانها و کامپایلر** است. با حذف ویژگیهای پیشرفته، این زبان بستری فراهم میکند تا دانشجو یا برنامهنویس بتواند بر اصول بنیادی مانند **تعریف متغیر، کنترل جریان برنامه و ساختار توابع** تمرکز کند. در نتیجه میتوان گفت *MiniC* نسخهای آموزشی و سادهشده از *C* است که **تنها بخشهای ضروری** و **بنیادین** آن را در بر میگیرد. در این سوال نیز **تضمین** خواهد شد که کدهایی که به عنوان **ورودی** به **مبهمساز** شما داده میشود از **نوع** *MiniC* هستند و **فاقد** پیچیدگیهای معمول زبان برنامه نویسی *C* میباشند. به مثالهای زیر از این زبان کوچولو، توجه کنید:
```c example1.mc c
int sum(int n) {
int s;
s = 0;
int i;
i = 1;
while (i <= n) {
s = s + i;
i = i + 1;
}
return s;
}
void main() {
int result;
result = sum(5);
print(result);
}
```
```c example2.mc c
int fib(int n) {
if (n <= 1) {
return n;
} else {
return fib(n - 1) + fib(n - 2);
}
}
void main() {
int i;
for (i = 0; i < 6; i = i + 1) {
print(fib(i));
}
}
```
```c example3.mc c
void main() {
int n;
read(n);
if (n % 2 == 0) {
print(0);
} else {
print(1);
}
}
```
</details>
<details class="grey">
<summary>
**معرفی آبگوشت پارسر** `abgoosh_parser`
</summary>
# **ساختار کلی پروژه** `abgoosht_parser`
**پروژهی** `abgoosht_parser` در واقع یک چارچوب ساده و قابل توسعه برای مبهمسازی و بازنویسی **برنامههای** *MiniC* است. این چارچوب، شامل اجزایی برای **تجزیه کد** *(Parsing)،* **تبدیل** *(AST Transforming)* و **تولید مجدد کد** *(Code Generation)* میباشد. ساختار پوشهبندی پروژه بهگونهای است که شفافیت ماژولها حفظ شده و شما به راحتی میتواند اجزای مختلف را گسترش دهید.
در هستهی این سیستم، ابزارهایی مانند `c_parser.py` و `c_ast.py` قرار دارند که مسئول ایجاد *AST* از کد *MiniC* و تعریف ساختار آن هستند. **درخت نحوی انتزاعی** *(AST)* در این پروژه بهشکل مجموعهای از کلاسهای پایتونی تعریف شده که بازتاب دقیق **ساختارهای نحوی زبان** *MiniC* است. **فایل** `c_parser.py`، **تجزیهگر اصلی** است که با استفاده از **ابزارهای** `lex` و `yacc` است. این فایلها با کمک `c_lexer.py` *(تحلیلگر واژگانی)* به ورودی زبان *MiniC* معنا میبخشند و با استفاده از `c_parser.py` آن را به **درختی نحوی انتزاعیاش** تبدیل میکنند. **ساختار کلی** `abgoosht_parser` به شکل زیر است:
```
abgoosht_parser
├── __init__.py
├── _ast_gen.py
├── _build_tables.py
├── _c_ast.cfg
├── ast_transforms.py
├── c_ast.py
├── c_generator.py
├── c_lexer.py
├── c_parser.py
├── ply
└── plyparser.py
```
**فایلهای** `c_lexer.py` و `c_parser.py` **هستهی اصلی تحلیل زبانی** را تشکیل میدهند؛ اولی وظیفهی شناسایی واژهها *(کلیدواژهها، عملگرها، شناسهها و...)* را دارد و دومی با تکیه بر گرامر **زبان** *MiniC* ساختار نحوی برنامه را ایجاد میکند. **درختهای نحوی** *(AST)* در قالب **کلاسهایی** که در `c_ast.py` تعریف شدهاند نمایش داده میشوند. این کلاسها خودکار از روی فایل پیکربندی `_c_ast.cfg` توسط اسکریپت `_ast_gen.py` تولید میشوند. اگر لازم باشد روی این درخت **تغییر** یا **مبهمسازی** صورت گیرد، **فایل** `ast_transforms.py` ابزارهای لازم را فراهم میکند.
در طرف دیگر، **فایل** `c_generator.py` وجود دارد که **مسیر معکوس** را طی میکند؛ یعنی *AST* کد را گرفته و دوباره به کد *MiniC* خوانا **بازتولید** میکند. ماژولهایی مثل `_build_tables.py` و `plyparser.py` نقش کمکی دارند و برای مدیریت جداول گرامری و تعامل سادهتر استفاده میشوند. به این ترتیب، **مجموعهی این فایلها** زنجیرهای کامل میسازند: از د*ریافت کد خام MiniC، تولید AST، اعمال تغییرات احتمالی روی آن و در نهایت بازگردانی یا تولید خروجی دلخواه.*
# **فایل** `generators.py` **و تبدیل AST به کد C**
یکی از اجزای مهم پروژه، **بخش تولید کد** یا *Generator* است. ماژول `c_generator.py` شامل **کلاسی** به نام `CGenerator` است که یک **ویزیتور** *AST* محسوب میشود. این کلاس تمام **رئوس** *AST* را پیمایش میکند و برای هر راس، کدی معادل در زبان *MiniC* تولید میکند. کدی که شما در پروژه اولیه دریافت میکنید شامل یک کلاس سادهتر به نام `CustomGenerator` است که از `CGenerator` ارثبری میکند و در این سوال، این فایل قابل تغییر نخواهد بود.
این بخش از پروژه از **دیزاین پترن ویزیتور** *(Visitor)* استفاده میکند. این الگو به ما اجازه میدهد عملیات مختلف *(مثلاً تولید کد، بررسی، تبدیل)* را روی کد آنها اعمال کنیم. هر راس در *AST* با **متدی از جنس** `visit_*` هندل میشود و این شیوهی به شما این امکان را خواهد داد که با پیادهسازی ویزیتورهای شخصیسازی شده، عملیات **ابهامسازی کد** را پیادهسازی کند.
```python generators.py python
from abgoosht_parser.c_generator import CGenerator
class CustomGenerator(CGenerator):
def __init__(self):
super().__init__()
self.indent_level = 0
def generate_code(ast):
generator = CustomGenerator()
return generator.visit(ast)
```
# **فایل** `parser.py` **و تبدیل کد C به AST**
در ابتدای هر پردازش، باید کد *MiniC* **تجزیه** شود. این مسئولیت بر عهدهی `c_parser.py` است که با استفاده از توابع `parse_code` و `parse_file` که **ساختار اولیه** `abgoosht_parser` قرار داده شدهاند، در واقع رابطی ساده برای فراخوانی این تجزیهگر فراهم میکنند. ورودی آنها کدی متنی به **زبان** *MiniC* است و خروجی، *AST* مربوط به کد به شکل یک **درخت** *AST* خواهد بود. **تبدیل کد** به *AST،* گام اول هر نوع **تحلیل** و **ابهامسازی** روی کد ورودی است. این *AST* تولید شده سپس توسط **تکنیکهای مختلف** که جلوتر توسط شما پیادهسازی میشوند، قابل **اصلاح** یا **بازنویسی** است.
```python parser.py python
from abgoosht_parser import c_parser, c_ast
def parse_code(code):
parser = c_parser.CParser()
ast = parser.parse(code)
return ast
def parse_file(filename):
with open(filename, 'r') as f:
code = f.read()
return parse_code(code)
```
# **فایل** `transformer.py` **تبدیل ساختار AST**
برای **دستکاری** *AST،* ابزار `Transformer` در **پروژه اولیه** تعریف شده است. این کلاس با دریافت لیستی از **تکنیکهای تبدیل** *(Transformation Techniques)،* آنها را به ترتیب روی *AST* اعمال میکند. **هر تکنیک،** یک **کلاس ویزیتور** جدید است که **راسهای خاصی** از *AST* را شناسایی و بازنویسی میکند. این طراحی مبتنی بر **الگوی طراحی استراتژی** *(Strategy Pattern)* است؛ چراکه هر تکنیک به عنوان یک *استراتژی متغییر* عمل میکند و **میتوان به دلخواه تکنیکها را اضافه، حذف یا جابجا کرد.**
**کلاس** `Transformer` باعث شده **ترکیب** و **اجرای چندین تکنیک** بهشکل ساده و پایپلاین ممکن شود. به عنوان مثال، میتوان *تکنیکی برای حذف کد مرده، تکنیکی برای تغییر نام متغیرها و تکنیکی برای بازآرایی بلوکهای شرطی* را به ترتیب اجرا کرد و در نهایت *AST* نهایی را تولید نمود.
```python transformer.py python
class Transformer:
def __init__(self, techniques=None):
self.techniques = techniques or []
def transform(self, ast):
current_ast = ast
for technique in self.techniques:
current_ast = technique.visit(current_ast)
if current_ast is None:
raise ValueError(f"Technique {technique.__class__.__name__} returned None")
return current_ast
def apply_transformations(ast, techniques):
transformer = Transformer(techniques)
return transformer.transform(ast)
```
</details>
<details class="green">
<summary>
**پیادهسازی آبگوشت** `obfuscator`
</summary>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *AliasGenerator* **و فایل** `alias_generator.py`
</summary>
**این تکنیک** روی بخشهایی از کد که **متغیرها** در آن تعریف شدهاند، عمل میکند. هر متغیری که تعریف میشود، دقیقا بعد از آن یک متغیر جدید با نام اصلی به علاوه پسوند `_alias` ساخته میشود. مثلاً اگر **متغیری به نام** `count` باشد، بلافاصله **متغیری به نام** `count_alias` تعریف میشود. **نوع داده** این متغیر جدید **دقیقاً همان نوع متغیر اصلی** است و **مقدار اولیهاش** به صورت مستقیم برابر با **همان متغیر اصلی** قرار میگیرد *(مثل یک اشارهگر به مقدار همان متغیر).*
```python obfuscator/techniques/alias_generator.py python
from abgoosht_parser.c_ast import NodeVisitor
class AliasGenerator(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `AliasGenerator` از **فایل** `alias_generator.py` هستید.
این عملیات باید **برای تمام متغیرهای تعریفشده در بدنه** توابع انجام میشود. پس از **اجرای این تکنیک،** کدی تولید میشود که در آن هر متغیر، یک **نسخه اضافی** با **نامی مشابه ولی پسوند** `_alias` دارد که دقیقا همان مقدار و نوع را نگه میدارد. این باعث میشود **تحلیل** و **درک نحوه** استفاده از متغیرها سختتر شود، زیرا چندین نام مختلف برای همان داده وجود دارد، **اما عملکرد برنامه کاملاً بدون تغییر باقی میماند.** به مثال زیر از این تکنیک ابهامسازی توجه کنید:
+ **کد** *MIniC* **اولیه:**
```c alias_generator_example.mc c
int main(){
int a = 10;
int b = 20;
int c = a + b;
printf("Hello world\n");
return 0;
}
```
+ **کد مبهم شده با تکنیک** *AliasGenerator:*
```c alias_generator_example.mc c
int main(){
int a = 10;
int a_alias = a;
int b = 20;
int b_alias = b;
int c = a + b;
int c_alias = c
printf("Hello world\n");
return 0;
}
```
- پس از ابهام سازی **سه متغیر جدید** با **نامهای** `a_alias`,`b_alias` و `c_alias` به کد ساخته شده اضافه شدهاند که **مقادیری برابر با متغیر اصلی** خود دارند و **از یک نوع** هستند.
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *ExprComplexifier* **و فایل** `expr_complexifier.py`
</summary>
**این تکنیک** روی عبارات محاسباتی در کد تمرکز دارد و تلاش میکند که **عبارات ساده** را به **شکلهای پیچیدهتر** ولی **معادل** تبدیل کند. هدف این است که نتیجه نهایی همان باشد **ولی ظاهر کد پیچیدهتر شود** و **خواندن آن سختتر شود.**
در این بخش **تنها دو مورد از اعمال ریاضی** مد نظر میباشند. در مواردی که **عملگر جمع** (`+`) در یک عبارت باینری دیده شود، آن عبارت به **فرم معادلی** تبدیل میشود که از عملیات بیت به بیت استفاده میکند: مقدار اصلی **به صورت** `(left ^ right) + ((left & right) << 1)` **جایگزین** میشود. در واقع عبارت `a + b` به ` (a XOR b) + ((a AND b) shifted left by 1)` تغییر میکند که **از نظر محاسباتی معادل جمع معمولی است اما نوشتار آن بسیار پیچیدهتر است.** در مواردی که **عمل ضرب** (`*`) بین یک **عدد صحیح** و **مقدار** `2` باشد، این عبارت با **یک شیفت چپ** (`<< 1`) معادل **جایگزین** میشود. **برای مثال،** `2 * x` **یا** `x * 2` **تبدیل به** `x << 1` **خواهد شد.**
```python obfuscator/techniques/expr_complexifier.py python
from abgoosht_parser.c_ast import NodeVisitor
class ExprComplexifier(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `ExprComplexifier` از **فایل** `expr_complexifier.py` هستید.
در نهایت **توجه داشته باشید** که از این تکنیک درهمسازی در این سوال **فقط این دو مورد تبدیل مورد نیاز است** و اگر **عملگرهای دیگری** دیده شوند یا شرایط فوق **برقرار نباشد،** عبارت **بدون تغییر** باقی میماند. به مثالهای زیر توجه کنید:
+ **کد** *MIniC* **اولیه:**
```c expr_complexifier_example.mc c
int main(){
int ans = 2 + 5;
int output = ans * 2;
return 0;
}
```
+ **کد مبهم شده با تکنیک** *ExprComplexifier:*
```c expr_complexifier_example.mc c
int main(){
int ans = (2 ^ 5) + ((2 & 5) << 1);
int output = ans << 1;
return 0;
}
```
+ همانطور که مشاهده میکنید، پس از اعمال این روش ابهامسازی، **عبارات ریاضی** نسبت به کد اولیه **پیچیدهتر** شده **اما دقیقا از لحاظ مقدار اولیه یکسان میباشند.**
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *DeadCodeInserter* **و فایل** `dead_code.py`
</summary>
**این تکنیک** درون بلوکهای کد، بخصوص بخشهایی که چند دستور پشت سر هم قرار دارند، **کدهایی اضافه میکند که هیچگاه اجرا نمیشوند.** این کدهای اضافه در این سوال، **به شکل یک شرط** `if` **با شرط همیشه نادرست** (`if (0)`) و **یک حلقه** `for` **با شرط آغاز و پایانی که هیچگاه وارد حلقه نمیشود** (`for (;0;)`) هستند.
```python obfuscator/techniques/dead_code.py python
from abgoosht_parser.c_ast import NodeVisitor
class DeadCodeInserter(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `DeadCodeInserter` از **فایل** `dead_code.py` هستید.
این دو **کد مرده** *(Dead Code)* به شکل زیر خواهند بود:
+ **شرط** `if` **با مقدار و بدنه** `0` که **هیچگاه** اجرا **نخواهد شد** و تغییری در ساختار اصلی کد به وجود **نخواهد آورد:**
```
if (0)
{
0;
}
```
+ **حلقه** `for` با **بدنهای خالی** که **هیچگاه اجرا نخواهد شد** و این کد مرده نیز تغییری در ساختار اصلی کد به وجود **نیاورده** اما باعث **پیچیدهتر شدن** و **ناخواناتر شدن** کد میشود:
```
for (; 0;)
{
}
```
**توجه داشته باشید** که در تکنیکی که در این سوال پیادهسازی میکنید، باید **دقیقا در ابتدای هر بلاکی که دارای بدنه است** *(مانند for بالا بدنهاش خالی نیست)* **یک شرط مرده** و **دقیقا در انتهای بلاک یک شرط حلقه مرده** را اضافه کند. **به مثال زیر از ابهامسازی کد توجه کنید:**
+ **کد** *MIniC* **اولیه:**
```c dead_code_example.mc c
int main(){
int ans = 2 + 5;
int output = ans * 2;
if(ans > 10){
ans = 10;
}
print(output);
return 0;
}
```
+ **کد مبهم شده با تکنیک** *DeadCodeInserter:*
```c dead_code_example.mc c
int main(){
if (0)
{
0;
}
int ans = 2 + 5;
int output = ans * 2;
if(ans > 10){
if (0)
{
0;
}
ans = 10;
for (; 0;)
{
}
}
print(output);
return 0;
for (; 0;)
{
}
}
```
+ همانطور که مشاهده میگنید، در کد مبهم شده در ابتدای هر بلاک کد **یک شرط** `if` **مرده** و در انتهای هر بلاک کد **یک حلقه** `for` **مرده** درج شده است که **خوانایی کد را کاهش** اما **عملکرد کد** را نسبت به کد اولیه **بدون تغییر** نگه میدارد.
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *FunctionSplitter* **و فایل** `function_splitter.py`
</summary>
**در این تکنیک مبهمسازی** باید ابتدا، **تابعهای کمی تا حدودی بزرگ** (یعنی توابعی که بیش از دو دستور دارند)، به **دو بخش تقریباً مساوی** تقسیم میشوند. **نیمهی اول** همان بدنهی اصلی تابع باقی میماند و **نیمهی دوم** در یک **تابع جدید** با **نامی مشتقشده** از نام **تابع اصلی** (دقیقا به شکل `<نام_تابع>_split_<شمارنده>`) قرار میگیرد. این تابع کمکی جدید باید تمامی پارامترهای تابع اصلی را به همراه متغیرهایی که در نیمه دوم استفاده شدهاند اما در نیمه اول تعریف شدهاند، به عنوان پارامتر دریافت کند.
```python obfuscator/techniques/function_splitter.py python
from abgoosht_parser.c_ast import NodeVisitor
class FunctionSplitter(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `FunctionSplitter` از **فایل** `function_splitter.py` هستید.
**توجه داشته باشید** که نام تابع کمکی و پارامترهای اضافه باید **دقیقاً مطابق این الگو** ساخته شوند تا در سیستم داوری قابل تشخیص باشند. **پارامترهای اضافه شده** باید از **نوع** و **مشخصات همان متغیرهای اصلی** کپی میشوند، بدون ایجاد تغییرات اضافهای که **در سیستم داوری مورد پذیرش قرار نخواهند گرفت.** تابع اصلی باید پس از اجرای نیمه اول، با ارسال پارامترهای لازم، تابع کمکی را فراخوانی کند. اگر **نوع بازگشتی تابع اصلی غیر** `void` باشد، **فراخوانی تابع کمکی** باید در **یک دستور بازگشت** (`return`) قرار گیرد، در غیر این صورت فقط فراخوانی به تنهایی در بدنه اضافه میشود.
**نتیجه نهایی،** کدی است که **ساختار توابع بزرگ** را به **مجموعهای از توابع کوچکتر** تقسیم میکند که با یکدیگر در تعاملاند. این کار باعث میشود درک جریان کنترل و ردیابی مقادیر در کد **دشوارتر** شود، بدون آنکه منطق برنامه تغییر کند. **به مثال زیر از این روش ابهامسازی توجه کنید:**
+ **کد** *MIniC* **اولیه:**
```c function_splitter_example.mc c
int add_and_print(int a, int b) {
int sum = a + b;
int square = sum * sum;
printf("sum: %d\n", sum);
printf("square: %d\n", square);
return sum;
}
```
+ **کد مبهم شده با تکنیک** *FunctionSplitter:*
```c function_splitter_example.mc c
int add_and_print_split_0(int a, int b, int sum, int square)
{
printf("sum: %d\n", sum);
printf("square: %d\n", square);
return sum;
}
int add_and_print(int a, int b)
{
int sum = a + b;
int square = sum * sum;
return add_and_print_split_0(a, b, sum, square);
}
```
+ در مثال بالا، **تابع اولیه** `add_and_print` به **دو تابع** تقسیم شده است. **تابع اولیه** `add_and_print` که **پارامترها** و **خروجیاش** تغییری **نکرده است** و **تابع** `add_and_print_split_0` که **نیمهی دوم** کدهای بدنهی تابع اولیه را داراست. به این صورت، کد اولیه به دو تابع تقسیم شده که **تحلیل** و **خواناییش** را **کاهش** میدهد.
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *MisleadingComments* **و فایل** `misleading_comments.py`
</summary>
**در این تکنیک،** در هر بلوک کد که **مجموعهای از دستورات** را در خود دارد *(و بلوکی با بدنه خالی نیست)،* **دقیقاً یک کامنت گمراهکننده** از بین کامنتهای زیر اضافه خواهد شد. متن کامنت از بین قالبهای ثابت و مشخص انتخاب میشود و به شکل زیر خواهند بود:
+ `// optimization level={}`
+ `// todo: refactor this loop`
+ `// warning: potential overflow at line {}`
+ `// debug: value of x is unknown`
+ `// temporary hack, remove later`
همانطور که مشاهده میکنید، **کامنتهای اول** و **سوم** دارای یک *Placeholder* میباشند، که باید با **یک مقدار عددی** جایگزین شوند. **مقدار عددی** این شمارنده در ابتدا از مقدار `0` شروع شده و با **مشاهده هر بلاک کد که خالی نباشد،** مبهمسازی شما باید **به ترتیب** و **به صورت چرخشی** هر کدام از کامنتها را در **ابتدای آن بلاک کد** درج کرده و **مقدار شمارنده را یکی افزایش دهد.**
```python obfuscator/techniques/misleading_comments.py python
from abgoosht_parser.c_ast import NodeVisitor
class MisleadingComments(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `MisleadingComments` از **فایل** `misleading_comments.py` هستید.
این کامنتها **به صورت رشته متنی** در ابتدای هر بلاک کد به گونهای درج میشود که ترتیب اجرای دستورات **تغییر نکند.** هدف این تکنیک افزودن کامنتهایی است که هیچ ارتباط واقعی یا فنی با کد **ندارند** و صرفاً باعث گمراه کردن خواننده یا تحلیلگر کد میشوند، **بدون اینکه تاثیری روی عملکرد برنامه داشته باشند.** به مثال زیر توجه کنید:
+ **کد** *MIniC* **اولیه:**
```c misleading_comments_example.mc c
void compute(int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += i;
}
if (sum > 50) {
printf("Large sum: %d\n", sum);
} else {
printf("Small sum: %d\n", sum);
}
while (sum > 0) {
sum--;
}
{
int temp = sum * 2;
printf("Temp: %d\n", temp);
}
}
int main() {
compute(10);
{
int a = 5;
int b = 10;
if (a < b) {
printf("a < b\n");
}
}
return 0;
}
```
+ **کد مبهم شده با تکنیک** *MisleadingComments:*
```c misleading_comments_example.mc c
void compute(int n)
{
// optimization level=5;
int sum = 0;
for (int i = 0; i < n; i++)
{
// optimization level=0;
sum += i;
}
if (sum > 50)
{
// todo: refactor this loop;
printf("Large sum: %d\n", sum);
}
else
{
// warning: potential overflow at line 2;
printf("Small sum: %d\n", sum);
}
while (sum > 0)
{
// debug: value of x is unknown;
sum--;
}
{
// temporary hack, remove later;
int temp = sum * 2;
printf("Temp: %d\n", temp);
}
}
int main()
{
// debug: value of x is unknown;
compute(10);
{
// warning: potential overflow at line 7;
int a = 5;
int b = 10;
if (a < b)
{
// todo: refactor this loop;
printf("a < b\n");
}
}
return 0;
}
```
+ در مثال بالا، **ترتیب چرخشی درج کامنتها** و **کامنتهایی** که دارای *Placeholder* هستند مشخص شده است. **توجه داشته باشید** که این ترتیب، **بر اساس ترتیب پیمایش** *dfs* ای بر روی **ساختار درخت** *AST* کد میباشد. به همین دلیل است که **اولین کامنت بیهوده افزوده شده، کامنت** `// optimization level=0;` میباشد.
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *VariableRenamer* **و فایل** `rename_vars.py`
</summary>
**در این تکنیک،** تمامی **نامهای متغیرهای محلی** و **پارامترهای توابع** بهصورت کامل **بازنامگذاری** میشوند به طوری که **هر نام یکتا** به **یک نام جدید با طول دقیقا هشت حرف انگلیسی** *(حروف بزرگ و کوچک)* تبدیل میشود. **این بازنامگذاری ثابت است؛** یعنی هر بار که یک نام مشخص در کد دیده شود، به همان **نام جدید اختصاص یافته تغییر مییابد** و **تمامی ارجاعات** به آن متغیر در جایگاههای مختلف کد نیز **اصلاح** میشود. نکته مهم این است که **نام توابع تغییر نمیکند** و فقط **متغیرها** و **پارامترهای** توابع تحت این تبدیل قرار میگیرند.
```python obfuscator/techniques/rename_vars.py python
from abgoosht_parser.c_ast import NodeVisitor
class VariableRenamer(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `VariableRenamer` از **فایل** `rename_vars.py` هستید.
**نامهای جدید کاملاً تصادفی ساخته میشوند،** مثلاً به شکل `AbcDefGh`، اما در کل برنامه **ثابت** و **یکسان** باقی میمانند تا **رفتار کد حفظ شود** و فقط **ابهام در نامگذاری** متغیرها ایجاد شود. این روش باید بهگونهای پیادهسازی شود که **هیچ تغییری در ساختار** و **عملکرد اصلی** برنامه **رخ ندهد** و تنها باعث گمراهی خواننده کد از طریق تغییر اسامی متغیرها شود.
+ **کد** *MIniC* **اولیه:**
```c rename_vars_example.mc c
int add(int a, int b) {
int sum = a + b;
int square = sum * sum;
printf("sum=%d, square=%d\n", sum, square);
return sum;
}
int main() {
int x = 5;
int y = 10;
int result = add(x, y);
printf("result=%d\n", result);
return 0;
}
```
+ **کد مبهم شده با تکنیک** *VariableRenamer:*
```c rename_vars_example.mc c
int add(int hQZrHNIb, int aAblsZPU)
{
int nZqxayZQ = hQZrHNIb + aAblsZPU;
int KMgwRWmn = nZqxayZQ * nZqxayZQ;
printf("sum=%d, square=%d\n", nZqxayZQ, KMgwRWmn);
return nZqxayZQ;
}
int main()
{
int iNVTbmjf = 5;
int rcfEdJgf = 10;
int yhEbwcjU = add(iNVTbmjf, rcfEdJgf);
printf("result=%d\n", yhEbwcjU);
return 0;
}
```
+ همانطور که در مثال بالا مشاهده میکنید، **اسامی تمامی متغیرها** و **پارامترهای توابع** و **همچنین تمام استفادههای آنها** در کد بازنویسی شدهاند و به رشتههایی با **مقادیر تصادفی** تبدیل شدهاند تا **خوانایی کد را کاهش دهند.**
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *ControlFlowFlattener* **و فایل** `control_flow_flattener.py`
</summary>
**در این تکنیک مبهمسازی**، جریان کنترل در تابع اصلی (تابع `main`) به صورت عادی دنبال نمیشود، بلکه به کمک یک **متغیر وضعیت** *(State variable)* و یک **ساختار** `switch-case` **درون یک حلقهی بینهایت بازنویسی** میشود. در ابتدای بدنه تابع، **یک متغیر جدید** به نام `__cf_state` تعریف و مقدار اولیهی آن صفر قرار داده میشود. **هر دستور در بدنهی اصلی تابع** به یک `case` متناظر تبدیل میشود که در آن، ابتدا **دستور اصلی** اجرا شده و سپس **مقدار متغیر** `__cf_state` به **شمارهی دستور بعدی** تغییر میکند.
در این ساختار، یک `switch` **روی مقدار** `__cf_state` وجود دارد که مشخص میکند **کدام بخش از کد** باید اجرا شود. این `switch` **داخل یک حلقهی** `while(1)` قرار داده میشود تا پس از هر تغییر وضعیت، اجرای **دستورات بعدی** ادامه پیدا کند. در مواردی که یک دستور از نوع `return` باشد، دیگر نیازی به تغییر وضعیت نیست و همانجا جریان اجرا خاتمه پیدا میکند.
```python obfuscator/techniques/control_flow_flattener.py python
from abgoosht_parser.c_ast import NodeVisitor
class ControlFlowFlattener(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `ControlFlowFlattener` از **فایل** `control_flow_flattener.py` هستید.
به این ترتیب، **ساختار اصلی کد** به جای توالی سادهای از دستورات، به یک **ماشین حالت** *(State Machine)* تبدیل میشود که **دنبال کردن جریان اجرای آن برای انسان** و **ابزارهای تحلیل ایستا** بسیار دشوارتر است. این کار بدون تغییر در منطق برنامه، باعث **افزایش سطح ابهام** و **سختتر شدن درک اجرای واقعی کد** میشود. به مثال ساده زیر توجه کنید:
+ **کد** *MIniC* **اولیه:**
```c control_flow_flattener_example.mc c
int main() {
int a = 5;
int b = 10;
return a + b;
}
```
+ **کد مبهم شده با تکنیک** *ControlFlowFlattener:*
```c control_flow_flattener_example.mc c
int main() {
int __cf_state = 0;
while (1) {
switch(__cf_state) {
case 0:
a = 5;
__cf_state = 1;
break;
case 1:
b = 10;
__cf_state = 2;
break;
case 2:
return a + b;
}
}
}
```
+ **این روش مبهمسازی،** کد بسیار سادهی اولیه را به **ماشین حالتی** تبدیل کرده است که **دقیقا همان عملکرد** را در سطح کد خواهد داشت اما **خوانایی** و **دنبال کردن جریان** اجرای کد را **بسیار پیچیده** کرده است.
</details>
<details class="grey">
<summary>
**پیادهسازی تکنیک** *OpaquePredicate* **و فایل** `opaque_predicate.py`
</summary>
**در این تکنیک مبهمسازی** هر دستور **ساده** داخل بلاکهای کد (دستوراتی که جزو دستورات `if`, `while`, `return`, `break`, `continue`, `switch` و **تعریف متغیرها نیستند**) بهجای اینکه مستقیماً اجرا شود، داخل یک **شرطِ همیشهصادق** `1 == 1` قرار میگیرد تا خواندن و تحلیل کد سختتر شود. **همهٔ دستورات واجد شرایط** *(غیر از مواردی که پیشتر اشاره شد)* همیشه و بدون تغییر اضافهتری با یک `if` احاطه میشوند. شرط `if` مورد استفاده ثابت و ساده است (`1 == 1`) تا رفتار اجرا هیچگاه تغییر نکند ولی ساختار کد پیچیدهتر و پر از شاخههای بیاثر شود.
```python obfuscator/techniques/opaque_predicate.py python
from abgoosht_parser.c_ast import NodeVisitor
class OpaquePredicate(NodeVisitor):
pass
```
+ **توجه کنید** که برای پیادهسازی این تکنیک شما صرفا مجاز به تغییر **کلاس** `OpaquePredicate` از **فایل** `opaque_predicate.py` هستید.
تبدیل باعث میشود **بلوکهای کد** با شاخههای بیاثر و **شرطهای همیشهحقیقی** احاطه شوند؛ این امر خوانایی و تحلیل ایستا را **کاهش** میدهد بدون اینکه منطق یا جریان دادهٔ برنامه تغییر کند. چنین تغییری برای ابزارهای سادهٔ آنالیز یا بررسی دستی، ردیابی مسیرهای اجرا و یافتن رابطهٔ بین دستورها را **دشوارتر** میکند. **به مثال تغییر پیش و پس از اِعمال این تکنیک توجه کنید:**
+ **کد** *MIniC* **اولیه:**
```c opaque_predicate_example.mc c
int main() {
int a = 5;
int b = 10;
int c = a + b;
printf("%d\n", c);
return 0;
}
```
+ **کد مبهم شده با تکنیک** *OpaquePredicate:*
```c opaque_predicate_example.mc c
int main() {
int a = 5;
int b = 10;
int c = a + b;
if (1 == 1) {
printf("%d\n", c);
}
return 0;
}
```
+ **در کد ابهام شده،** میتوان مشاهده کرد که **تنها دستور** `printf` توسط یک شرط `if` **همیشه درست** ابهام شده و دستورات دیگر به دلیل اینکه **تعاریف متغیرها** هستند، **بی تغییر باقی ماندهاند.**
</details>
</details>
# **آنچه باید آپلود کنید**
+ **توجه**: سیستم داوری این سوال برای نمرهدهی، ابتدا با استفاده از **مبهمساز شما،** کدهای ورودی در هر تستکیس را مبهم کرده و سپس *AST* **کد مبهمشده توسط شما** را با *AST* **کدی که به درستی از قبل ابهام شده** مقایسه کرده و در صورتی که **شباهت** *AST* این دو کد، **اکیداً بیشتر از ۸۰ درصد باشد،** نمرهی آن تستکیس مشخص به کد ارسالی شما **تعلق خواهد گرفت.**
+ **توجه**: سیستم داوری در هر مرحله، **کدهای مبهمشده توسط شما** را اجرا کرده و از **عدم تغییر عملکرد** این کدها نسبت به **کدهای ورودی اولیه،** اطمینان حاصل میکند. در صورتی که **هر گونه تغییری** در روند اجرای **کدهای مبهمشده توسط شما** *(مثل کامپایل ارورها یا خروجیهای متفاوت)* رخ دهد، حتی در صورت برآورده شدن **شرط پیشین** و **شباهت بیش از ۸۰ درصدی،** ارسال شما برای تستکیس مشخص شده **امتیازی در بر نخواهد داشت و نمره صفر دریافت خواهد کرد.**
+ **توجه**: پس از پیادهسازی موارد خواسته شده، **کل فایلهای پروژه** را زیپ کرده و ارسال کنید.
+ **توجه**: شما مجاز به **افزودن فایل جدیدی** در این ساختار **نیستید** و تنها باید تغییرات را در فایلهای موجود اعمال کنید.
+ **توجه**: که نام فایل _Zip_ اهمیتی **ندارد**.
آبگوشت فناوری!
> اشتباهات برنامهنویسها معمولاً **بخشودنی** هستند. آنهادر طول کار روزانه **باگهای زیادی** ایجاد میکنند یا در طراحی نرمافزار دچار **خطا** میشوند؛ خطاهایی که اغلب در **بازبینی کد** *(Code Review)* توسط افراد باتجربهتر **شناسایی** و **برطرف** میشوند. با این حال، برخی اشتباهات به دلیل پیامدهای جدی و هزینههای بالایی که به همراه دارند نابخشودنی هستند...
پس از استقبالات فراوان از [سری اول رویداد **#المپیکفناوری پردیس**](https://quera.org/events/techolympics-0307)، دستاندرکاران مجموعه **پارک علموفناوری پردیس** به فکر بازگشایی یک **فروشگاه جدید** و مجهز برای برطرف کردن **نیازهای زندگی برنامهنویسانه**ی اهالی این پارک فناوری و به ویژه شرکتکنندگان [سری جدید مسابقات **#المپیکفناوری پردیس**](https://quera.org/events/techolympics-0407) افتادند. این فروشگاه که پس از راهاندازی، بعدها توسط **پردیسیون** *(Pardision)* به **پردیس شاپ** *(Pardis Shop)* معروف شد هرآنچه که یک برنامهنویس در زندگیاش به آن نیاز داشت، از **انواع و اقسام تنقلات** تا **کیبوردهای مکانیکال** به آنها ارائه میکرد.
**پردیس شاپ،** که به مرور زمان به یکی از **معروفترین فروشگاههای ناحیه فناوری پردیس** تبدیل شد، همواره از نبود یک **فروشگاه آنلاین** برای ارائه **تجربهای بهتر** و **خدماتی بیشتر** به مشتریانش رنج برده است. **علی میو** *(Ali Meow)* یکی از **پردیسیونی** است که به واسطه **علاقهاش به گربهها** *(که محبوبترین حیوانات خانگی نزد برنامهنویسها هستند)* طرفداران زیادی دارد، به عنوان **مسئول طراحی وبسایت پردیس شاپ** انتخاب شده و کارش را از مدتها قبل از برگزاری **سری دوم المپیکفناوری پردیس** شروع کرده است.

**علی میو** برای **مدیریت کاربران** پردیس شاپ **سه مدل جداگانه** طراحی کرده است: *یکی برای مدیران، یکی برای مشتریان و دیگری برای فروشندگان.* در ابتدا همهچیز برای علی و دستاندرکاران پردیس شاپ **ساده** و **قابلمدیریت** بهنظر میرسید، اما با رشد این فروشگاه و افزایش نیازها، کمکم **مشکلات بزرگی** نمایان شد. هر تغییری در سیستم این آنلاین شاپ به **سه جای مختلف** سرایت میکرد و نگهداری کدها به **کابوسی واقعی** تبدیل شده بود! **اشتباه علی** که دیگر تنها یک اشتباه ساده در طراحی نرمافزار نبود، حال به یک **اشتباه بسیار بزرگ** و **نابخشودنی** تبدیل شده است...
# **پروژهی اولیه**
برای دانلود **پروژهی اولیه** روی [این لینک](/contest/assignments/84123/download_problem_initial_project/305475/) کلیک کنید.
<details class="yellow">
<summary>**ساختار فایلها**</summary>
```
pardis_shop
├── users_app
│ ├── <mark class="yellow" title="این پوشه را تغییر دهید"> > migrations < </mark>
│ │ ├── 0001_initial.py
│ │ └── utils
│ │ └── user_migration.py
│ ├── __init__.py
│ ├── <mark class="yellow" title="این فایل را تغییر دهید"> > models.py < </mark>
│ └── ...
├── manage.py
└── ...
```
</details>
<details class="grey">
<summary>**راهاندازی پروژه**</summary>
برای اجرای پروژه، باید **پایتون و ابزار** _pip_ را از قبل نصب کرده باشید.
- ابتدا **پروژهی اولیه** را دانلود و از حالت فشرده خارج کنید.
- در پوشهی اصلی پروژه، یک **محیط مجازی پایتون** _(venv)_ ایجاد و فعال کنید:
```bash terminal terminal
python -m venv venv
source venv/bin/activate # در ویندوز: venv\Scripts\activate
```
- دستور زیر را برای **نصب نیازمندیها** در پوشهی اصلی پروژه اجرا کنید:
```bash terminal terminal
pip install -r requirements.txt
```
- برای **اجرای پروژه** *Django* دستور زیر را در مسیر پوشهی اصلی پروژه اجرا کنید:
```bash terminal terminal
python manage.py runserver
```
در صورت اجرای موفق، **یک لینک** در خروجی نمایش داده میشود که میتوانید **آن را در مرورگر باز کنید.**
- برای **اجرای مایگریشنها** و ایجاد جداول پایگاه داده، دستور زیر را اجرا کنید:
```bash terminal terminal
python manage.py migrate
```
- برای **بارگذاری دادههای نمونه** از **فایل** *fixture،* دستور زیر را اجرا کنید:
```bash terminal terminal
python manage.py loaddata users_fixture.json
```
</details>
# **جزئیات پروژه**
اکنون وقت **بازطراحی مدلهای پردیس شاپ** رسیده است. شما باید **ساختار دادهای** طراحی شده توسط **علی میو** که در **قالب پروژه اولیه** در دسترس شما قرار گرفته را **اصلاح کنید** و آنرا **بهبود ببخشید.** در طراحی اولیهی پروژه، **اپلیکیشنی** با نام `users_app` مسئولیت **مدیریت انواع کاربران** را بر عهده دارد. در این پیادهسازی، **سه مدل مجزا برای کاربران** با نقشهای متفاوت تعریف شده است:
- **مدل** `AdminUser`
- **مدل** `CustomerUser`
- **مدل** `VendorUser`
[**طراحی دیاگرام** *(ER Diagram)*](https://en.wikipedia.org/wiki/Entity%E2%80%93relationship_model) این مدلها به صورت زیر است:

هر یک از این مدلها اطلاعات خاص خود را ذخیره میکنند. با وجود برطرف کردن نیازهای ابتدایی، اکنون مشخص شده که این ساختار از لحاظ طراحی پایگاهداده و اصول مهندسی نرمافزار با **نقصهای جدی** همراه است. وجود **چندین مدل** برای انواع کاربران منجر به **افزونگی، دشواری در توسعه و مشکلات نگهداری** شده است.
**علی میو** پس از مواجهه با مشکلات نگهداری و توسعه در ساختار قبلی، تصمیم به بازطراحی کامل سیستم کاربری گرفته است. شما در این پروژه قرار است بخشهای زیر از **سیستم مدیریت کاربران** را طراحی کنید.
<details class="red">
<summary>**پیادهسازی مدلهای جدید**</summary>
شما باید با استفاده از تصویر زیر که نشان دهنده **ساختار مدلهای جدید** است، **مدلهای جدید کاربران** را پیادهسازی کنید:

**مدلهایی که باید پیادهسازی کنید:**
- **مدل مشترک** `CustomUser`.
- **سه مدل دیگر به نامهای** `AdminProfile`، `CustomerProfile`و `VendorProfile` .
**رابطههای مدلها:**
**مدل** `CustomUser` به عنوان **مدل اصلی کاربر** عمل میکند و شامل فیلدهای مشترک تمام کاربران است. هر یک از **مدلهای پروفایل** (`AdminProfile`، `CustomerProfile`، `VendorProfile`) با **مدل** `CustomUser` **رابطه یکبهیک** دارد، به این معنی که:
- هر کاربر **فقط یک پروفایل** از هر نوع میتواند داشته باشد.
- هر پروفایل **فقط به یک کاربر** متصل است.
- با حذف کاربر، **پروفایل مرتبط نیز حذف میشود.**
- **هر پروفایل** دارای `related_name` **منحصر به فرد** است تا بتوان به راحتی از کاربر به پروفایل دسترسی داشت.
- شما باید این مدلها را **در کنار مدلهای قبلی** (`AdminUser`، `CustomerUser`، `VendorUser`) و در **همان فایل** `models.py` تعریف کنید.
- به عنوان `username` در مدل `CustomUser` میتوانید از **ایمیل کاربران** استفاده کنید.
- برای **کاربران ادمین** در مدل `CustomUser` باید **ویژگیهای** `is_staff` و `is_superuser` مقداردهی شوند.
- توجه داشته باشید که **مدلهای دیگر** موجود در عکس بالا که ربطی هم به مدل یوزر ندارند را **نیازی نیست** در پیادهسازی درنظر بگیرید، **برای این مدلها امتیازی درنظر گرفته نخواهد شد.**
</details>
<details class="blue">
<summary>**ایجاد فایل مایگریشن**</summary>
پس از **پیادهسازی مدلهای جدید،** با استفاده از دستور زیر، **فایل مایگریشن** را بسازید:
```bash terminal terminal
python manage.py makemigrations
```
در نتیجه این عملیات، **فایلی مشابه فایل زیر** ساخته خواهد شد:
```
0002_user_adminprofile_..._.py
```
</details>
<details class="green">
<summary>**پیادهسازی تابع انتقال دادهها**</summary>
در **فایل** `users_app/migrations/utils/user_migration.py` **تابعی با نام** **`migrate_users_forward`** وجود دارد. این تابع وظیفه دارد تا **اطلاعات کاربران** را از **مدلهای قبلی** (`AdminUser`, `CustomerUser`, `VendorUser`) به **مدلهای جدید** منتقل کند و دادهها را بهدرستی در `User` و پروفایلهای مرتبط ذخیره کند.
شما باید **این تابع را تکمیل کرده** و در فایل مایگریشنی که ساختهاید (`0002_...`) با استفاده از `migrations.RunPython(...)` این تابع را اجرا کنید. برای این کار، در بخش `operations` این فایل، دستور زیر را اضافه کنید:
```python 0002_user_adminprofile_..._.py python
from users_app.migrations.utils.user_migration import migrate_users_forward
...
migrations.RunPython(migrate_users_forward, reverse_code=migrations.RunPython.noop)
```
- **توجه:** نیازی به برگشتپذیر بودن این مایگریشن **نیست.**
</details>
# **آنچه باید آپلود کنید**
- **توجه**: پس از پیادهسازی موارد خواسته شده، **کل فایلهای پروژه** بهجز پوشهی `venv` و **فایل** `db.sqlite3` را زیپ کرده و ارسال کنید.
- **توجه**: شما **مجاز به افزودن فایل جدید** به جز **فایل مایگرشن** خواسته شده در این ساختار **نیستید** و تنها باید تغییرات را در فایلهای موجود اعمال کنید.
- **توجه**: که نام فایل _Zip_ اهمیتی **ندارد**.
- **توجه:** در پایان فراموش نکنید که مدل `CustomUser` را در فایل `settings.py` با `AUTH_USER_MODEL = 'users_app.CustomUser'` اضافه کنید.
مدلهای نابخشودنی
> **تیم فنی کوئرا** در تمامی این سالها، همواره سعی کرده تا **خارقالعادهترین** ویژگیها را به **زیباترین** شکل به کاربران در تمام بخشهای کوئرا از جمله **کانتست، کالج و بوتکمپ** ارائه کند. **لود زیاد تیم فنی کوئرا** در تمام این سالها باعث شده تا توسعهدهندگانش همواره **تنها و تنها** به فکر توسعه فیچرهای مختلف باشند. آن هم **به هر قیمتی که شده!** حتی به قیمت خلق **کدبِیسی** *(Code Base)* **بسیار کثیف** و **آشفته**...
**ممزواد** *(Mamzavad)،* **مدیرفنی** *(CTO)* و از **محبوبترین** شخصیتهای سوالات مسابقات برنامهنویسی کوئرا، به تازگی و پس از پیوستن شخصیت محبوب دیگر، یعنی **آمین** *(Aaaamin)،* به تیم فنیاش در کوئرا، شدیدا دچار **تحول** شده و بالاخره تصمیم گرفته است تا **کدبِیس کثیف کوئرا** را بعد از نزدیک به یک دهه **بازنویسی** کند! ممزواد که قرار است به زودی **لود سنگین** و **جدید** دیگری را نیز که [**سری دوم #المپیکفناوری پردیس**](https://quera.org/events/techolympics-0407) است را تحمل کند، تصمیم گرفته تا **توسعه فیچرهای جدید** را مستقل از **بازنویسی کدبِیس کثیف کوئرا** انجام دهد.
اگر از **روزهای نخستین کوئرا** چیزی به یاد داشته باشید، **کوئرای اولیه** از **پروژهی قدیمی** و **معروف** [**شریفجاج** *(Sharif Judge)*](https://github.com/mjnaderi/Sharif-Judge) شکل گرفته و به ترتیب در تمام یک دهه فعالیتش، بخشهای زیادی از آن **بازنویسی** شده و ویژگیهای بسیاری که امروزه نیز شما در حال استفاده از بسیاری از ویژگیها هستید، به آن **افزوده** شدند. ممزواد نیز که تصمیم به **بازنویسی کدبِیس کوئرا** افتاده اینبار اما با الهام از **روزهای قدیمی کوئرا،** این پروژه جدید را با نام رمزی *جاجِ مَمجَجاد* تعریف کرده است. آمین، که خود نیز از الهامبخشان شروع پروژه **بازنویسی کدبیس کوئرا** و **جاج مَمجَجاد** بوده است میخواهد خود شخصا دست به کار شود تا چرخ این پروژه نیز مانند سایر پروژههای تعریف شده در کوئرا، به حرکت در بیاید.

از آنجایی که کار **جاج مَمجَجاد** باید همزمان با آمادهسازی **ویژگیهای جدید کوئرا** برای سری جدید مسابقات المپیکفناوری پردیس پیشروی کند و **آمین** نیز به تازگی و پس از مدت زیادی **کوئراکاری،** حسابی خسته شده و تصمیم به **سفری طولانی مدت** و **بیبازگشت** از مبدا کوئرا گرفته، با تهیه **مستنداتی کامل** از تمام **اندپوینتها** *(Endpoints)* و **سرویسها** در کوئرا، پروژه مهم و اساسی **جاج مَمجَجاد** را به شما، که با عبور از تمام سوالات سخت و مردافکن این مسابقه تا به این سوال رسیدهاید، سپرده است.
# **پروژه اولیه**
برای دانلود **پروژهی اولیه** روی این [این لینک](/contest/assignments/84123/download_problem_initial_project/305471/) کلیک کنید.
<details class="yellow">
<summary>
**ساختار فایلها**
</summary>
```
mini-quera
├── Dockerfile
├── <mark class="orange" title="این اپلیکیشن باید پیاده سازی شود">accounts</mark>
├── <mark class="orange" title="این اپلیکیشن باید پیاده سازی شود">contests</mark>
├── <mark class="orange" title="این اپلیکیشن باید پیاده سازی شود">core</mark>
├── db.sqlite3
├── docker-compose.yml
├── entrypoint.sh
├── <mark class="orange" title="این اپلیکیشن باید پیاده سازی شود">judge</mark>
├── judge-worker.Dockerfile
├── <mark class="orange" title="این اپلیکیشن باید پیاده سازی شود">lms</mark>
├── manage.py
├── mini_quera
├── <mark class="orange" title="این اپلیکیشن باید پیاده سازی شود">plagiarism</mark>
├── <mark class="orange" title="این اپلیکیشن باید پیاده سازی شود">problems</mark>
├── requirements.txt
├── static
├── submission_files
├── <mark class="orange" title="این اپلیکیشن باید پیاده سازی شود">submissions</mark>
└── templates
```
</details>
<details class="grey">
<summary>
**راهاندازی پروژه**
</summary>
برای **اجرای پروژه،** باید **پایتون** و **ابزار داکر** را از قبل نصب کرده باشید.
+ ابتدا فایل **پروژهی اولیه** را از قسمت لینک بالا **دانلود** و **استخراج** کنید.
+ برای اجرای پروژه با **داکر کامپوز،** دستور زیر را در **مسیر پوشهی اصلی پروژه** اجرا کنید. این دستور سرویسهای *پروژهی جنگویی جاج مَمجَجاد، Celery، RabbitMQ و محیط داوری کد* را در سیستم شما بالا میآورد:
```bash docker docker
docker compose up --build
```
+ بعد از بالا آمدن کانتینرها و توسعه مدلهای جدید، **مایگریشنهای دیتابیس** را **اجرا** کنید تا **جداول مورد نیاز** ایجاد شوند:
```bash docker docker
docker compose exec web python manage.py migrate
```
+ برای **جمعآوری فایلهای استاتیک** و **آمادهسازی پروژه،** دستور زیر را اجرا کنید:
```bash docker docker
docker compose exec web python manage.py collectstatic --noinput
```
+ در صورتی که **تغییراتی در وابستگیها** ایجاد شد یا سرویسها به مشکل برخوردند، کانتینرها را میتوانید با استفاده از دستور زیر مجدداً اجرا کنید:
```bash docker docker
docker compose up --build -d
```
+ پس از اجرای موفق، **وباپلیکیشن جاج مَمجَجاد** از طریق **آدرس** http://localhost:8000 در دسترس است و **ادمین پنل مدیریت سرویس** *RabbitMQ* از طریق **آدرس** http://localhost:15672 با **نام کاربری** `rabbitmq_user` و **رمز عبور** `rabbitmq_pass` قابل مشاهده خواهد بود.
</details>
# **جزئیات پروژه**
**پروژه جاج مَمجَجاد،** دقیقا عملکردی مشابه آنچه امروزه شما به عنوان کوئرا میشناسید را دارد. این پروژه امکانات مختلفی از جمله **احراز هویت کاربران، ساخت و مدیریت سوالات و تستکیسها، ساخت مسابقات و کلاسهای جدید و افزودن سوالات ساخته شده به آنها، ارسال کد و داوری در سرویس جدا و مخصوص** `judge_worker` که با داشتن وابستگیهای مختلف، امکان اجرای **کدهای پایتونی، جاوا، سی و سیپلاسپلاس** را امکان پذیر میکند.
معماری این پروژه بهصورت **ماژولار** و **مبتنی بر صف** طراحی شده است؛ **سرویس** `web` مسئول مدیریت کاربران و اندپوینتهای مختلف است، یک **ورکر** *Celery* با استفاده از *RabbitMQ* وظایف **داوری** را بهصورت **غیرهمزمان** اجرا میکند و **سرویس** `judge_worker` **محیطی ایزوله** و **امن** برای اجرای کدها فراهم میسازد. این ساختار امکان داوری سریع، ایمن و مقیاسپذیر را برای زبانهای مختلف برنامهنویسی فراهم کرده و جاج مَمجَجاد را بیشتر از قبل به **کوئرای واقعی** شبیهتر میکند.
<details class="olive">
<summary>
**معرفی سرویسهای پروژه** `mini-quera`
</summary>
## **سرویس** `web`
**سرویس** `web` **هسته اصلی جاج مَمجَجاد** است و مسئول ارائه **APIها** و **رابط کاربری** میباشد. این سرویس با *Gunicorn* اجرا شده و تمامی **درخواستهای کاربران** را مدیریت میکند. **جاج مَمجَجاد** به **سرویس** `rabbitmq` و `judge_worker` متصل است تا **وظایف داوری** و **پردازش پسزمینه** را ارسال کند. **تمامی اندپوینتها، شامل ثبت نام، ارسال کد و دسترسی به مسابقات و مشکلات، از طریق این سرویس قابل دسترسی هستند.**
## **سرویس** `celery`
**سرویس** `celery` **وظایف پسزمینه** و زمانبر جاج را مدیریت میکند. این سرویس از *RabbitMQ* به عنوان **پیامرسان** *(Message Broker)* استفاده میکند تا تسکها را **بدون مسدود کردن سرویس** `web` اجرا کند. وظایفی مانند **داوری کدها** و **بررسی سرقت ادبی** توسط این سرویس پردازش میشوند. با استفاده از این سرویس، **اجرای همزمان چندین تسک** بدون ایجاد تداخل یا کندی در پروژه امکانپذیر است.
## **سرویس** `judge_worker`
**سرویس** `judge_worker` برای **اجرای کدهای کاربران در محیط ایزوله** طراحی شده است. این سرویس با نصب داشتن **انواع وابستگیهای مورد نیاز** برای اجرای امن *کدهای پایتون، C، C++ و جاوا* استفاده میشود.خروجی و وضعیت اجرای کدها به سرویس `web` و `celery` بازگردانده میشود تا **نتایج داوری ثبت** و **نمایش** داده شوند. **سرویس** `judge_worker` **به طور مستقل عمل میکند** تا از تاثیر اجرای کدهای کاربران بر سرویس اصلی **جلوگیری** شود.
## **سرویس** `rabbitmq`
**سرویس** `rabbitmq` **پیامرسان** *(Message Broker)* اصلی پروژه است که ارتباط بین **سرویسهای** `web`، `celery` و `judge_worker` را برقرار میکند. این سرویس وظیفه مدیریت صفها و **ارسال پیامهای مربوط به وظایف پسزمینه** را دارد. **بدون** *RabbitMQ،* Celery **نمیتواند** تسکها را دریافت و اجرا کند و **هماهنگی بین سرویسها از بین میرود. سرویس** *RabbitMQ* امکان پردازش همزمان چندین تسک و مدیریت اولویتها را فراهم میکند.
</details>
---
<details class="teal">
<summary>
**معرفی و پیادهسازی اپلیکیشن** `core` **(پیادهسازی اندپوینت** `health` **الزامی است!)**
</summary>
**اپلیکیشن** `core` **باید شامل اندپوینت سلامت سرویس، مدیریت سیگنالهای سیستم و تنظیمات تعامل با سرویس** `celery` برای پردازش پسزمینه باشد.
## **بررسی سلامت سرویس** (`/health/`)
**این اندپوینت وضعیت کلی سرویس را بیان میکند و برای اطمینان از فعال بودن سرویس مورد استفاده قرار میگیرد. هیچ ورودی پیچیدهای نیاز ندارد و پاسخ آن ساده و سریع است.**
```bash terminal terminal
curl -X GET http://localhost:8000/health/
```
**پاسخ موفق:**
```bash terminal terminal
{
"status": "ok"
}
```
## **جدول پیادهسازی**

</details>
<details class="blue">
<summary>
**معرفی و پیادهسازی اپلیکیشن** `accounts`
</summary>
**اپلیکیشن** `accounts` مسئول **مدیریت کاربران سامانه** است. این اپ که **بر پایه مدل** `AbstractUser` جنگویی باید توسعه یابد، **امکان ثبت نام کاربران جدید** و **مشاهده فهرست کاربران موجود** را فراهم میکند. ثبت نام **برای همه کاربران** قابل دسترسی است، در حالی که مشاهده لیست کاربران **فقط برای مدیران** امکانپذیر است.
## **ثبت نام کاربر جدید** (`/api/accounts/register/`)
**ورودی** اندپوینت ثبت نام کاربر جدید (`/api/accounts/register/`) شامل **فیلدهای** `username`، `email`، `password`، `password2` و `role` است. مقدار فیلد `role` مشخص میکند کاربر چه نقشی در سامانه خواهد داشت، به عنوان مثال اگر مقدار آن برابر با `admin` باشد، کاربر با **سطح دسترسی مدیریتی** (`is_staff`) ایجاد میشود و در غیر این صورت سطح دسترسی معمولی مانند دانشجو یا استاد به او اختصاص مییابد. در صورت موفقیت در ثبت نام، پاسخ شامل **اطلاعات کاربر** تازه ایجاد شده به همراه شناسه کاربر است. اگر **مقدارهای** `password` و `password2` با یکدیگر مطابقت نداشته باشند، سیستم **خطای اعتبارسنجی** را بازمیگرداند تا کاربر از اصلاح ورودی خود مطمئن شود.
```bash terminal terminal
curl -X POST http://localhost:8000/api/accounts/register/ \
-H "Content-Type: application/json" \
-d '{
"username": "example_user",
"email": "user@example.com",
"password": "password123",
"password2": "password123",
"role": "student"
}'
```
+ در صورتی که **رمز عبور** و **تکرار آن** مطابقت **نداشته** باشند، **خطای اعتبارسنجی** با متن زیر بازگردانده میشود:
```bash terminal terminal
{
"password": "رمز عبور مطابقت ندارد."
}
```
+ در **پاسخ موفقیتآمیز ثبت نام،** اطلاعات کاربر جدید **بدون رمز عبور** به شکل زیر نمایش داده میشود:
```bash terminal terminal
{
"id": 1,
"username": "example_user",
"email": "user@example.com",
"role": "student"
}
```
## **مشاهده لیست کاربران** (`/api/accounts/users/`)
**اندپوینت مشاهده لیست کاربران** (`/api/accounts/users/`) تنها در صورتی قابل دسترسی است که **کاربر دارای نقش مدیر** باشد. در صورت پاسخ موفق، **لیستی از کاربران** موجود را بازمیگرداند که هر کاربر با **شناسه، نام کاربری، ایمیل و نقش خود** نمایش داده میشود. در صورتی که کاربر نقش مدیر نداشته باشد یا توکن معتبر ارسال نکند، پاسخ شامل **خطای عدم دسترسی** خواهد بود تا از مشاهده اطلاعات سایر کاربران **جلوگیری** شود.
```bash terminal terminal
curl -X GET http://localhost:8000/api/accounts/users/ \
-H "Authorization: Bearer <YOUR_TOKEN>"
```
+ در **پاسخ موفق** برای مشاهده لیست کاربران، فهرست کاربران موجود بازگردانده میشود، **هر آیتم شامل** `id`, `username`, `email` و `role` است:
```bash terminal terminal
[
{
"id": 1,
"username": "example_user",
"email": "user@example.com",
"role": "student"
},
{
"id": 2,
"username": "admin_user",
"email": "admin@example.com",
"role": "admin"
}
]
```
+ در صورتی که **کاربر غیرمدیر** به این اندپوینت دسترسی پیدا کند، خطای زیر بازگردانده میشود:
```bash terminal terminal
{
"detail": "You do not have permission to perform this action."
}
```
## **جدول پیادهسازی**

</details>
<details class="pink">
<summary>
**معرفی و پیادهسازی اپلیکیشن** `problems`
</summary>
**اپلیکیشن** `problems` **مسئول مدیریت سوالات** *(Problems)* و **تستکیسهای** *(TestCases)* **جاج** است. این اپ **امکان ایجاد و مشاهده سوالات و مدیریت تستکیسها** را فراهم میکند. ایجاد **سوالات و تستکیسها** فقط برای مدیران امکانپذیر است، اما مشاهده جزئیات و فهرست مسائل برای همه کاربران **قابل دسترسی** است.
## **ایجاد سوال جدید** (`/api/problems/create/`)
**ورودی** اندپوینت **ایجاد سوال جدید** (`/api/problems/create/`) شامل **فیلدهای** `title` به عنوان عنوان سوال، `description` برای توضیح کامل سوال، `input_description` و `output_description` برای تشریح دادههای ورودی و خروجی و **محدودیتهای زمان** (`time_limit`) و **حافظه** (`memory_limit`) است. **تنها مدیران** میتوانند از این اندپوینت استفاده کنند و در پاسخ موفق، **تمام جزئیات مسئله شامل** `id`، `title`، `description`، `input_description`، `output_description`، `time_limit`، `memory_limit` و آرایهای از **تستکیسهای سوال** (`testcases`) بازگردانده میشود. در صورتی که **مقادیر** `time_limit` یا `memory_limit` **نامعتبر** باشند، **خطای اعتبارسنجی** مناسب صادر میشود تا اطمینان حاصل شود که محدودیتها مثبت و معتبر هستند.
```bash terminal terminal
curl -X POST http://localhost:8000/api/problems/create/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{
"title": "Sum of Numbers",
"description": "Calculate the sum of given numbers",
"input_description": "Two integers",
"output_description": "Sum of integers",
"time_limit": 1,
"memory_limit": 64
}'
```
+ در صورت موفقیت، **اطلاعات سوال** ایجاد شده بازگردانده میشود:
```bash terminal terminal
{
"id": 1,
"title": "Sum of Numbers",
"description": "Calculate the sum of given numbers",
"input_description": "Two integers",
"output_description": "Sum of integers",
"time_limit": 1,
"memory_limit": 64,
"testcases": []
}
```
+ در صورت **ارسال مقدار نامعتبر** برای `time_limit` یا `memory_limit`، **خطای اعتبارسنجی** بازگردانده میشود:
```bash terminal terminal
{
"time_limit": ["Time limit must be greater than 0 seconds."]
}
```
## **مشاهده فهرست سوالات** (`/api/problems/`)
**اندپوینت مشاهده فهرست سوالات** (`/api/problems/`) خروجی را به صورت **آرایهای از سوالات** بازمیگرداند که هر عنصر شامل `id`، `title`، `description`، `input_description`، `output_description`، `time_limit`، `memory_limit` و **آرایهای از تستکیسها** (`testcases`) است. این اندپوینت **برای همه کاربران** قابل دسترسی است و امکان مشاهده **سوالات** و **تستکیسهای مرتبط با آنها** را فراهم میکند. **هر تستکیس شامل** `id`، `input_data`، `expected_output` و `is_sample` است که مشخص میکند **آیا تستکیس در نتیجه اصلی محاسبه میشود و یا صرفا به عنوان نمونه است و در سیستم داوری مورد استفاده قرار نمیگیرد.**
```bash terminal terminal
curl -X GET http://localhost:8000/api/problems/
```
+ **نمونه پاسخ موفق:**
```bash terminal terminal
[
{
"id": 1,
"title": "Sum of Numbers",
"description": "Calculate the sum of given numbers",
"input_description": "Two integers",
"output_description": "Sum of integers",
"time_limit": 1,
"memory_limit": 64,
"testcases": [
{
"id": 1,
"input_data": "2 3",
"expected_output": "5",
"is_sample": true
}
]
}
]
```
## **مشاهده جزئیات سوال** (`/api/problems/<int:pk>/`)
**اندپوینت مشاهده جزئیات سوال** (`/api/problems/<int:pk>/`) **اطلاعات یک مسئله مشخص را بر اساس شناسه آن ارائه میدهد و شامل تمام فیلدهای مسئله مانند** `id`، `title`، `description`، `input_description`، `output_description`، `time_limit`، `memory_limit` **و آرایهای از تستکیسها** (`testcases`) است. این اندپوینت **برای همه کاربران** قابل دسترسی است و اگر مسئلهای با شناسه مشخص وجود نداشته باشد، **پاسخ شامل خطای استاندارد** `{"detail": "Not found."}` خواهد بود.
```bash terminal terminal
curl -X GET http://localhost:8000/api/problems/1/
```
+ **نمونه پاسخ موفق مشابه نمونه پاسخ فهرست سوالات است،** اما **فقط اطلاعات یک سوال** بازگردانده میشود.
## **ایجاد تستکیسها** (`/api/problems/testcases/create/`)
**ورودی** اندپوینت ایجاد نمونه داده تست (`/api/problems/testcases/create/`) **شامل** `problem` **به عنوان شناسه مسئله مرتبط،** `input_data`، `expected_output` و `is_sample` برای مشخص کردن نمونه بودن یا اصلی بودن تستکیس است *(تستکیسهای نمونه در سیستم داوری مورد استفاده قرار نخواهند گرفت).* **تنها مدیران** میتوانند از این اندپوینت استفاده کنند و در پاسخ موفق، **تمام جزئیات تستکیس** شامل `id`، شناسه مسئله (`problem`)، `input_data`، `expected_output` و `is_sample` بازگردانده میشود.
```bash terminal terminal
curl -X POST http://localhost:8000/api/problems/testcases/create/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{
"problem": 1,
"input_data": "2 3",
"expected_output": "5",
"is_sample": true
}'
```
+ در پاسخ موفق، **اطلاعات تستکیس ایجاد شده** بازگردانده میشود:
```bash terminal terminal
{
"id": 1,
"problem": 1,
"input_data": "2 3",
"expected_output": "5",
"is_sample": true
}
```
## **مشاهده تستکیسهای یک سوال** (`/api/problems/<int:problem_id>/testcases/`)
**اندپوینت مشاهده تستکیسهای یک سوال** (`/api/problems/<int:problem_id>/testcases/`) خروجی را **به صورت آرایهای از تستکیسهای مرتبط** به سوال مشخص شده بازمیگرداند و **هر تستکیس شامل** `id`، `input_data`، `expected_output` و `is_sample` است. **تنها مدیران** قادر به دسترسی به این اندپوینت هستند و در صورت تلاش کاربران غیرمجاز، **پاسخ شامل خطای** `{"detail": "You do not have permission to perform this action."}` خواهد بود.
```bash terminal terminal
curl -X GET http://localhost:8000/api/problems/1/testcases/ \
-H "Authorization: Bearer <YOUR_TOKEN>"
```
+ **نمونه پاسخ موفق:**
```bash terminal terminal
[
{
"id": 1,
"input_data": "2 3",
"expected_output": "5",
"is_sample": true
}
]
```
+ در صورت **عدم دسترسی کاربر غیرمدیر،** خطای زیر بازگردانده میشود:
```bash terminal terminal
{
"detail": "You do not have permission to perform this action."
}
```
## **جدول پیادهسازی**

</details>
<details class="yellow">
<summary>
**معرفی و پیادهسازی اپلیکیشن** `submissions`
</summary>
**اپلیکیشن** `submissions` مسئول **مدیریت ارسال کد کاربران** و **بررسی وضعیت** اجرای آنهاست. کاربران میتوانند کد خود را ارسال کنند و وضعیت اجرای آن شامل **موفقیت، خطا، خروجی و زمان اجرا** را مشاهده کنند.
## **ارسال کد برای اجرا** (`/api/submissions/submit/`)
**اندپوینت ارسال کد برای اجرا** (`/api/submissions/submit/`) **ورودی را به صورت** `multipart/form-data` دریافت میکند که **شامل فیلد** `problem` **برای مشخص کردن شناسه سوال،** `code_file` **به عنوان فایل کد کاربر و** `language` **برای تعیین زبان برنامهنویسی است.** کاربران ثبتنام شده با **توکن معتبر** میتوانند از این اندپوینت استفاده کنند و در پاسخ موفق، **شناسه ارسال** (`submission_id`) و **وضعیت اولیه** `submitted` بازگردانده میشود تا سیستم داوری بتواند ارسال را پیگیری کند. در صورتی که هر یک از فیلدها خالی یا نامعتبر باشند، **خطای مناسب شامل پیامهایی مانند** `This field is required.` یا `No file was submitted.` نمایش داده میشود.
سیگنالهای ایجاد شده توسط این اندپوینت وضعیت اجرای کد را در **سه وضعیت** `ACCEPTED` ,`FAILED` و `ERROR` ثبت میکنند. هر لاگ مرتبط با یک اجرای کد شامل **وضعیت کلی**، **خروجی تولید شده**، **خطا در صورت وجود** و **زمان اجرای کد** است. سیستم داوری باید با استفاده از سرویس `judge_worker` و **سیگنالهایی** که توسط **سرویس** `celery` پردازش خواهند شد، هنگام داوری سوال هر **تستکیس غیر نمونهای** را جداگانه اجرا کند و نتیجه آن با مقایسه خروجی واقعی و خروجی مورد انتظار ثبت شود. اگر **تمام** تستکیسها درست باشند، **وضعیت نهایی** `ACCEPTED` خواهد بود، در صورت **عدم شباهت خروجیها** در **حداقل یک تستکیس** وضعیت نهایی `FAILED` میشود و اگر اجرای کد با **خطا** مواجه شود یا **زبان پشتیبانی نشود، وضعیت** `ERROR` باید ثبت گردد.
```bash terminal terminal
curl -X POST http://localhost:8000/api/submissions/submit/ \
-H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-F "problem=1" \
-F "code_file=@solution.py" \
-F "language=python"
```
+ در صورت **موفقیتآمیز بودن ارسال،** پاسخ شامل شناسه ارسال و **وضعیت اولیه** است:
```bash terminal terminal
{
"submission_id": 42,
"status": "submitted"
}
```
+ در صورتی که دادههای ارسالی **نامعتبر** باشند، **خطاهای** زیر بازگردانده میشود:
```bash terminal terminal
{
"problem": ["This field is required."],
"code_file": ["No file was submitted."],
"language": ["This field is required."]
}
```
## **مشاهده وضعیت ارسال** (`/api/submissions/status/<int:submission_id>/`)
**اندپوینت مشاهده وضعیت ارسال** (`/api/submissions/status/<int:submission_id>/`) **تمام لاگهای یک ارسال** مشخص را برای کاربر ارسال بازمیگرداند**. هر لاگ شامل وضعیت اجرای کد** (`ACCEPTED`, `FAILED`, `ERROR`)، **خروجی تولید شده، خطا در صورت وجود، زمان اجرای کد و زمان ثبت لاگ** است. پاسخ به صورت لیست از لاگها بازگردانده میشود و ترتیب آن بر اساس **ترتیب صعودی زمان ثبت لاگ** (`created_at`) است. **تنها کاربر صاحب ارسال** یا **مدیر** با **توکن معتبر** میتواند به این اندپوینت دسترسی داشته باشد و اگر شناسه ارسال **وجود نداشته باشد** یا **متعلق به کاربر دیگری** باشد، پاسخ شامل **خطای** `{"error": "Submission not found"}` خواهد بود.
همچنین توجه داشته باشید که در این اندپوینت، **کلید** `output` شامل **نتایج اجرای کد برای تستکیسهای غیر نمونهی تعریف شده** برای هر سوال **به شکل دقیقا** `TestCase #n: {PASS, FAIL}` خواهد بود که همگی باید توسط `\n` به یکدیگر متصل شده و نمایش داده شوند.
```bash terminal terminal
curl -X GET http://localhost:8000/api/submissions/status/42/ \
-H "Authorization: Bearer <YOUR_TOKEN>"
```
+ **نمونه پاسخ موفق:**
```bash terminal terminal
[
{
"id": 6,
"submission": 42,
"status": "ERROR",
"output": null,
"error": "Some error text",
"created_at": "2025-09-25T06:07:17.911261Z"
},
{
"id": 7,
"submission": 42,
"status": "ACCEPTED",
"output": "TestCase #1: PASS\nTestCase #2: PASS",
"error": null,
"created_at": "2025-09-25T06:07:23.145892Z"
}
]
```
+ در صورتی که شناسه ارسال **وجود نداشته باشد** یا متعلق به کاربر دیگری باشد، خطای زیر بازگردانده میشود:
```bash terminal terminal
{
"error": "Submission not found"
}
```
## **جدول پیادهسازی**

</details>
<details class="red">
<summary>
**معرفی و پیادهسازی اپلیکیشن** `contests`
</summary>
**اپلیکیشن** `contests` **مسئول مدیریت مسابقات** است. این اپ **قابلیت ایجاد مسابقه جدید، مشاهده جزئیات مسابقه، فهرست مسابقات و مشاهده ردهبندی زنده مسابقات** را فراهم میکند. همه اندپوینتها **نیازمند احراز هویت کاربر** هستند و **تنها کاربران وارد شده** قادر به استفاده از آنها هستند.
## **ایجاد مسابقه جدید** (`/api/contests/create/`)
**ورودی** اندپوینت ایجاد مسابقه جدید (`/api/contests/create/`) **شامل فیلدهای** `name` **به عنوان نام مسابقه،** `start_time` **و** `end_time` **به صورت تاریخ و زمان** است. تمام این فیلدها باید معتبر باشند و شرط اصلی این است که `end_time` بعد از `start_time` باشد؛ در غیر این صورت **خطای اعتبارسنجی** با پیام `End time must be after start time` بازمیگرداند. در پاسخ موفق، **تمام اطلاعات مسابقه شامل** `id` و `name`، `start_time`، `end_time`، `description` نمایش دهد. این اندپوینت تنها برای کاربران وارد شده با **توکن معتبر** قابل دسترسی است و سریالایزر اعتبار دادهها را تضمین میکند.
```bash terminal terminal
curl -X POST http://localhost:8000/api/contests/create/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{
"name": "Sample Contest",
"start_time": "2025-09-14T10:00:00Z",
"end_time": "2025-09-14T12:00:00Z"
}'
```
+ **خطای اعتبارسنجی در صورت اشتباه بودن زمانها:**
```bash terminal terminal
{
"end_time": "End time must be after start time"
}
```
+ در پاسخ موفق، **جزئیات مسابقه** ایجاد شده برگردانده میشود:
```bash terminal terminal
{
"id": 1,
"name": "Sample Contest",
"start_time": "2025-09-14T10:00:00Z",
"end_time": "2025-09-14T12:00:00Z",
}
```
## **مشاهده جزئیات مسابقه** (`/api/contests/<int:pk>/`)
**اندپوینت مشاهده جزئیات مسابقه** (`/api/contests/<int:pk>/`) **اطلاعات کامل یک مسابقه** مشخص را بر اساس شناسه آن ارائه میدهد. خروجی شامل `id`، `name`، `start_time`، `end_time`، `description` میباشد. کاربران برای دسترسی باید وارد سامانه باشند و در صورت درخواست برای کانتستی که وجود ندارد، **پاسخ شامل خطای** `{"detail": "Not found."}` خواهد بود. این اندپوینت به کاربر امکان میدهد جزئیات کامل مسابقه را مشاهده کند.
```bash terminal terminal
curl -X GET http://localhost:8000/api/contests/1/ \
-H "Authorization: Bearer <YOUR_TOKEN>"
```
+ **در صورت عدم وجود مسابقه با شناسه مورد نظر، خطای زیر بازگردانده میشود:**
```bash terminal terminal
{
"detail": "Not found."
}
```
+ در پاسخ موفق، جزئیات مسابقه شامل تمام فیلدهای آن نمایش داده میشود.
## **مشاهده لیست مسابقات** (`/api/contests/`)
**اندپوینت مشاهده فهرست مسابقات** (`/api/contests/`) تمام مسابقات موجود در جاجِ مَمجَجاد را به صورت آرایهای بازمیگرداند. هر عنصر آرایه شامل `id`، `name`، `start_time`، `end_time`، `description` میباشد. این اندپوینت نیز محدود به کاربران وارد شده است و به آنها اجازه میدهد تمام مسابقات فعال و گذشته را مشاهده کنند.
```bash terminal terminal
curl -X GET http://localhost:8000/api/contests/ \
-H "Authorization: Bearer <YOUR_TOKEN>"
```
+ در پاسخ موفق، **آرایهای از اطلاعات مسابقات شامل** `id`, `name`, `start_time` و `end_time` بازگردانده میشود:
```bash terminal terminal
[
{
"id": 1,
"name": "Sample Contest",
"start_time": "2025-09-14T10:00:00Z",
"end_time": "2025-09-14T12:00:00Z"
},
{
"id": 2,
"name": "Another Contest",
"start_time": "2025-09-15T09:00:00Z",
"end_time": "2025-09-15T11:00:00Z"
}
]
```
## **مشاهده ردهبندی زنده مسابقه** (`/api/contests/<int:contest_id>/standings/live/`)
**اندپوینت مشاهده ردهبندی زنده مسابقه** (`/api/contests/<int:contest_id>/standings/live/`) امکان **دریافت امتیازات شرکتکنندگان** به صورت **لحظهای** *(Streaming)* را فراهم میکند. کاربران وارد شده میتوانند با ارسال **توکن معتبر،** جریان داده را در هر ثانیه دریافت کنند که شامل **آرایهای از دیکشنریها** است و هر دیکشنری **شامل کلید** `user` **و مقدار نام کاربری شرکتکننده و کلید** `score` **به عنوان امتیاز فعلی او میباشد.** اگر مسابقه با شناسه مورد نظر وجود نداشته باشد، پاسخ **شامل خطای** `{"detail": "No Contest matches the given query."}` خواهد بود. همچنین ردهبندی بر اساس **امتیازات بدست آمده از ارسالهای در سوالات مسابقه** مشخص شده **به صورت نزولی** است.
```bash terminal terminal
curl -N http://localhost:8000/api/contests/1/standings/live/ \
-H "Authorization: Bearer <YOUR_TOKEN>"
```
+ **در صورتی که مسابقهای با شناسه مورد نظر وجود نداشته باشد، خطای زیر بازگردانده میشود:**
```bash terminal terminal
{
"detail": "No Contest matches the given query."
}
```
+ **در پاسخ موفق، دادهها به صورت استریم هر ثانیه بروزرسانی میشوند:**
```bash terminal terminal
data: [{"user": "user1", "score": 100}, {"user": "user2", "score": 90}]
```
## **جدول پیادهسازی**

</details>
<details class="grey">
<summary>
**معرفی و پیادهسازی اپلیکیشن** `lms`
</summary>
**اپلیکیشن** `lms` **مسئول مدیریت کلاسها، درسها و مسائل مرتبط با درسها** است. این اپ قابلیت *ایجاد و مدیریت کلاسها، ایجاد درسها و مسائل و ثبتنام دانشآموزان در کلاسها* را فراهم میکند. همه اندپوینتها **نیازمند احراز هویت** هستند و **بسیاری از آنها فقط برای مدیران قابل دسترسی هستند.**
## **لیست و ایجاد کلاسها** (`/api/lms/classes/`)
**ورودی** اندپوینت لیست و ایجاد کلاسها (`/api/lms/classes/`) **شامل فیلد** `name` **به عنوان نام کلاس،** `teacher` **به عنوان شناسه استاد مرتبط با کلاس و** `students` **به صورت آرایهای از شناسههای دانشآموزان است.** این اندپوینت **تنها برای مدیران** قابل دسترسی است و در پاسخ موفق، تمام جزئیات کلاس شامل `id`، `name`، **شناسه استاد** (`teacher`) **و آرایه شناسههای دانشآموزان** (`students`) بازگردانده میشود. **نام کلاس نباید خالی باشد و استاد و دانشآموزان باید همگی موجود باشند.**
```bash terminal terminal
curl -X POST http://localhost:8000/api/lms/classes/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{
"name": "Sample Class",
"teacher": 1,
"students": [1]
}'
```
+ **در پاسخ موفق، جزئیات کلاس ایجاد شده برگردانده میشود:**
```bash terminal terminal
{
"id": 1,
"name": "Sample Class",
"teacher": 1,
"students": [1]
}
```
## **جزئیات کلاس** (`/api/lms/classes/<int:pk>/`)
**اندپوینت جزئیات کلاس** (`/api/lms/classes/<int:pk>/`) **اطلاعات کامل یک کلاس مشخص** را بر اساس شناسه آن ارائه میدهد و **شامل** `id`، `name`، **شناسه استاد** (`teacher`) **و آرایه دانشآموزان** (`students`) است. این اندپوینت **تنها برای مدیران** قابل دسترسی است و در صورتی که کلاس با شناسه مورد نظر وجود نداشته باشد، **پاسخ شامل خطای** `{"detail": "Not found."}` خواهد بود.
```bash terminal terminal
curl -X GET http://localhost:8000/api/lms/classes/1/ \
-H "Authorization: Bearer <YOUR_TOKEN>"
```
+ **در صورت عدم وجود کلاس، خطای زیر بازگردانده میشود:**
```bash terminal terminal
{
"detail": "Not found."
}
```
## **افزودن دانشآموز در کلاس** (`/api/lms/classes/<int:class_id>/enroll/`)
**اندپوینت افزودن دانشآموز در کلاس** (`/api/lms/classes/<int:class_id>/enroll/`) **شامل فیلد** `student_id` برای مشخص کردن شناسه دانشآموز است. **تنها مدیران** میتوانند از این اندپوینت استفاده کنند و در پاسخ موفق، پیغام `{"status": "enrolled"}` بازگردانده میشود. در صورتی که کلاس یا دانشآموز **وجود نداشته باشند،** پاسخ شامل خطای `{"error": "Class not found"}` یا `{"error": "Student not found"}` خواهد بود و اگر `student_id` ارسال نشود، خطای `{"error": "student_id is required"}` بازگردانده میشود. این اندپوینت امکان مدیریت ثبتنام دانشآموزان در کلاس مورد نظر را فراهم میکند.
```bash terminal terminal
curl -X POST http://localhost:8000/api/lms/classes/1/enroll/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{
"student_id": 2
}'
```
+ **در پاسخ موفق:**
```bash terminal terminal
{
"status": "enrolled"
}
```
+ **در صورت عدم وجود کلاس یا دانشآموز:**
```bash terminal terminal
{"error": "Class not found"}
{"error": "Student not found"}
```
+ **و اگر** `student_id` **ارسال نشود:**
```bash terminal terminal
{"error": "student_id is required"}
```
## **لیست و ایجاد درسها** (`/api/lms/lessons/`)
**ورودی اندپوینت لیست و ایجاد درسها** (`/api/lms/lessons/`) **شامل** `name` **به عنوان نام درس و** `class_obj` **به عنوان شناسه کلاس** مرتبط است. این اندپوینت **تنها برای مدیران** قابل دسترسی است و در پاسخ موفق، **تمام جزئیات درس شامل** `id`، `name` و **شناسه کلاس** (`class_obj`) بازگردانده میشود تا امکان مدیریت و پیگیری درسها فراهم شود.
```bash terminal terminal
curl -X POST http://localhost:8000/api/lms/lessons/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{
"name": "Lesson 1",
"class_obj": 1
}'
```
- **در پاسخ موفق، جزئیات درس جدید بازگردانده میشود.**
## **جزئیات درس** (`/api/lms/lessons/<int:pk>/`)
**اندپوینت جزئیات درس** (`/api/lms/lessons/<int:pk>/`) **اطلاعات کامل یک درس مشخص** را ارائه میدهد و **شامل** `id`، `name` و **شناسه کلاس مرتبط** (`class_obj`) است. **تنها مدیران** قادر به دسترسی به این اندپوینت هستند و در صورت **عدم وجود درس** با **شناسه مشخص،** پاسخ شامل خطای `{"detail": "Not found."}` خواهد بود.
```bash terminal terminal
curl -X GET http://localhost:8000/api/lms/lessons/1/ \
-H "Authorization: Bearer <YOUR_TOKEN>"
```
- **در صورت عدم وجود درس:**
```bash terminal terminal
{
"detail": "Not found."
}
```
## **لیست و ایجاد مسائل درس** (`/api/lms/lesson-problems/`)
**ورودی** اندپوینت لیست و ایجاد مسائل درس (`/api/lms/lesson-problems/`) **شامل** `title` **برای عنوان مسئله،** `lesson` **به عنوان شناسه درس مرتبط** و `problem` **به عنوان شناسه مسئله** دریافت میکند. **تنها مدیران** میتوانند از این اندپوینت استفاده کنند و در پاسخ موفق، جزئیات مسئله شامل `id`، `title`، **شناسه درس** (`lesson`) و **شناسه مسئله** (`problem`) بازگردانده میشود.
```bash terminal terminal
curl -X POST http://localhost:8000/api/lms/lesson-problems/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{
"title": "Problem 1",
"lesson": 1,
"problem": 1
}'
```
## **جدول پیادهسازی**

</details>
<details class="green">
<summary>
**معرفی و پیادهسازی اپلیکیشن** `plagiarism`
</summary>
**اپلیکیشن** `plagiarism` مسئول **بررسی شباهت کدهای ارسال شده** توسط کاربران و تشخیص موارد **سرقت ادبی کد** *(Code Plagiarism)* است. این اپ امکان **اجرای بررسی سرقت ادبی برای یک مسئله خاص** یا **تمامی ارسالها** و **مشاهده نتایج بررسیها** را فراهم میکند. **همه اندپوینتها فقط برای مدیران قابل دسترسی هستند.**
## **اجرای بررسی تقلب** (`/api/plagiarism/run/`)
**ورودی** اندپوینت اجرای بررسی تقلب (`/api/plagiarism/run/`) **شامل فیلد اختیاری** `problem_id` برای **محدود کردن بررسی به یک مسئله** مشخص است. اگر این فیلد ارسال نشود، بررسی **روی تمامی ارسالها** انجام میشود. **تنها مدیران** میتوانند از این اندپوینت استفاده کنند و در پاسخ موفق، **پیغام** `{"status": "Plagiarism check completed"}` بازگردانده میشود تا مشخص شود فرآیند بررسی به پایان رسیده است. **در پسزمینه، جاجِ مَمجَجاد تمامی ارسالها را مقایسه میکند** و با استفاده از **مقایسه ساختار درختی** *AST،***میزان شباهت بین هر جفت ارسال** را محاسبه میکند. اگر **درصد شباهت بین دو ارسال بیشتر یا مساوی** `70%` باشد، ارسالها **به عنوان تقلب** علامتگذاری *(Flagged)* میشوند. **این روش تضمین میکند که شباهتهای ساده ناشی از تغییر نام متغیر یا فرمتبندی، باعث تشخیص تقلب نادرست نشوند و تنها شباهتهای واقعی در ساختار کد مشخص شوند.**
```bash terminal terminal
curl -X POST http://localhost:8000/api/plagiarism/run/ \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
-d '{
"problem_id": 5
}'
```
+ اگر `problem_id` **ارسال نشود،** بررسی روی تمام ارسالها انجام میشود.
```bash terminal terminal
{
"status": "Plagiarism check completed"
}
```
## **مشاهده نتایج بررسی تقلب** (`/api/plagiarism/results/`)
**اندپوینت مشاهده نتایج بررسی تقلب** (`/api/plagiarism/results/`) خروجی را **به صورت آرایهای از نتایج** بازمیگرداند که **هر آیتم شامل** `id` به عنوان شناسه رکورد، `submission1` و `submission2` **به عنوان شناسههای دو ارسال مقایسه شده،** `similarity` **به صورت درصد شباهت بین دو کد،** `flagged` **به صورت بولین که مشخص میکند آیا دو ارسال به عنوان تقلب علامتگذاری شدهاند یا خیر و** `created_at` **به عنوان زمان ثبت نتیجه** است. **تنها مدیران** قادر به دسترسی به این اندپوینت هستند و در صورت عدم دسترسی کاربر غیرمدیر، **پاسخ شامل پیغام خطای** `{"detail": "You do not have permission to perform this action."}` خواهد بود. این خروجی به سیستم داوری امکان میدهد تا به صورت دقیق و خودکار تشخیص دهد کدام ارسالها از نظر شباهت کد **به هم نزدیک** هستند و **کدام موارد نیاز به بررسی دستی برای تعیین تقلب دارند.**
```bash terminal terminal
curl -X GET http://localhost:8000/api/plagiarism/results/ \
-H "Authorization: Bearer <YOUR_TOKEN>"
```
+ **در پاسخ موفق، آرایهای از نتایج بازگردانده میشود:**
```bash terminal terminal
[
{
"id": 1,
"submission1": 10,
"submission2": 15,
"similarity": 85.5,
"flagged": true,
"created_at": "2025-09-14T12:00:00Z"
},
{
"id": 2,
"submission1": 11,
"submission2": 12,
"similarity": 45.0,
"flagged": false,
"created_at": "2025-09-14T12:05:00Z"
}
]
```
+ **در صورت عدم دسترسی کاربر غیرمدیر:**
```bash terminal terminal
{
"detail": "You do not have permission to perform this action."
}
```
## **جدول پیادهسازی**

</details>
# **آنچه باید آپلود کنید**
+ **توجه**: **تمام مواردی** که در سیستم داوری سوال مورد ارزیابی قرار میگیرند، **به جزئیات** و **به صورت مفصل** در متن سوال به همراه ذکر مثالها توضیح داده شدهاند. مواردی که در متن سوال به انها اشارهای نشده است، مانند **نحوه پیادهسازی مدلها، سیگنالها، سریالایزرها و ...** در سیستم داوری به صورت مستقیم مورد ارزیابی قرار **نخواهند گرفت** و میتوانند به **دلخواه** شما، اما در **چارچوب سوال** پیادهسازی شوند.
+ **توجه**: سرویسهایی که در این سوال از آنها میتوانید استفاده کنید و در **فایل** `docker-compose.yml` تعریف شدهاند، همگی در **بخش معرفی سرویسهای پروژه** مشخص شده و **تعریف سرویس جدید** و یا **شخصی سازی سرویسهای موجود** با استفاده از تغییر فایل داکر کامپوز، **امکان پذیر نیست!**
+ **توجه**: شما در این سوال مجاز به ایجاد **هر گونه تغییرات جدیدی در داکرفایلها، فایل** `docker-compose.yml` **و تنظیمات از پیش انجام شدهی پروژه جنگویی نیستید** و پیادهسازیهای شما صرفا باید به **اپلیکیشنهای مشخص شده** در بخش ساختار فایلها **محدود** شود. **همچنین شما در این سوال مجاز به ایجاد اپلیکیشن جدیدی نیز نیستید.**
+ **توجه**: **کدهای وضعیت** در سیستم داوری دارای اهمیت بوده و بررسی خواهند شد. کدهای وضعیت زیر، کدهایی که هستند در پیادهسازی اندپوینتهای مختلف باید **به صورت منطقی** و **با توجه به اطلاعات هر بخش** مورد استفاده قرار گیرند:
- **کد** `403` – **Forbidden**: کاربر مجوز دسترسی به این منابع یا عملیات را ندارد.
- **کد** `400` – **Bad Request**: درخواست ارسال شده نامعتبر است یا دادهها ناقص هستند.
- **کد** `200` – **OK**: درخواست موفق بوده و پاسخ مورد انتظار بازگردانده شده است.
- **کد** `201` – **Created**: منبع جدید با موفقیت ایجاد شده است (مانند ارسال کد یا ساخت کلاس جدید).
- **کد** `401` – **Unauthorized**: کاربر احراز هویت نشده یا توکن معتبر ندارد.
- **کد** `404` – **Not Found**: منبع مورد نظر وجود ندارد یا کاربر دسترسی به آن ندارد.
+ **توجه**: پس از پیادهسازی موارد خواسته شده، **کل فایلهای را زیپ کرده و ارسال کنید.**
+ **توجه**: شما مجاز به افزودن فایل جدیدی در این ساختار **نیستید** و تنها باید تغییرات را در فایلهای موجود اعمال کنید.
+ **توجه**: که نام فایل _Zip_ اهمیتی **ندارد**.