کار ها:
+ نوشتن Decorator برای view ها (نمره: ۶۵ درصد)
+ نوشتن view برای فعال کردن پروژه فعلی (نمره: ۱۵ درصد)
+ نوشتن تابع has_permission (نمره: ۲۰ درصد)
---------------------------------------------------------------------------------------------------------
یک شرکت نرمافزاری میخواهد برای خود یک سیستم مدیریت پروژه بنویسد. بدین منظور سایتی با قابلیت های زیر طراحی شده است:
+ در این سایت چندین پروژه میتواند وجود داشته باشد.
+ هر پروژه میتواند چندین عضو داشته باشد.
+ هر کاربر میتواند در چند پروژه در نقش های مختلف عضویت داشته باشد.
از این [لینک](http://s9.picofile.com/file/8339632884/myproject_initial.zip.html) میتوانید source code های پروژه را دانلود کنید.
ساختار این پروژه به شکل زیر است:
```
src
├── manage.py
├── ProjectManager
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── projects
│ ├── admin.py
│ ├── apps.py
│ ├── decorators.py <--------
│ ├── models.py <--------
│ ├── activation_view.py <--------
│ ├── templates
│ │ └── projects
│ │ └── project_home.html
│ ├── tests.py
│ ├── urls.py
│ └── views.py
└── requirements.txt
```
مدلِ`ProjectMembership` که در فایل *models.py* نوشته شده است، عضویت یک فرد را در یک پروژه به همراه نقش او در پروژه نشان میدهد.
در فایل views.py نیز چندین view وجود دارد که اعمال مختلف قابل انجام روی پروژه را پیاده سازی کرده است.
این سایت بدین شکل کار میکند که همواره **فقط یکی** از پروژه های کاربر به عنوان "پروژه فعلی" او انتخاب شده است که از طریق صفحهی `index` نیز قادر به مشاهده این پروژه هستیم و تمامی اعمال مختلف مانند حذف پروژه و غیره، روی همین پروژه فعلی انجام میشوند.
نکته: یک کاربر در یک پروژه فقط یک بار و با یک نقش میتواند عضو باشد. بنابراین واژه های "پروژهی فعلی" و "عضویت فعلی" به یک معنا هستند.
حال از شما میخواهیم تغییرات زیر را در این پروژه انجام دهید.
### ۱. نوشتن activation_view:
این view شناسهی یک **پروژه** را به عنوان ورودی گرفته و آن را به عنوان پروژه فعلی انتخاب میکند. بدین منظور یک field در مدل ProjectMembership به نام is_current در نظر گرفته شده است که باید آن را مقدار دهی کند(`is_current=True`) و برای سایر عضویتها (ProjectMembership) نیز این field را False کند.
+ توجه کنید ممکن است به هر دلیلی مانند race condition، از قبل برای یک کاربر چندین عضویت با is_current=True وجود داشته باشد. بعد از اجرای این کد باید اطمینان حاصل شود که برای این user، فقط یک عضویت فعلی وجود دارد.
+ در صورتی که کاربر فعلی در پروژه با شناسه مورد نظر عضویت نداشت یا به کل پروژهای با شناسه مورد نظر وجود نداشت، باید یک پاسخ ۴۰۴ نشان دهید.
+ بعد از این که پروژه مورد نظر با موفقیت به عنوان پروژه فعلی برای کاربر فعلی انتخاب شد، این view باید به صفخه `index` (یعنی آدرس `/project`) redirect کند. در صفحهی index باید بتوانیم پروژه انتخاب شده را ببینیم.
### ۲. نوشتن تابع has_permission:
برای این بخش از سوال باید یک تابع به نام `has_permission(action)` در مدل `ProjectMembership` تعریف کنید. این تابع یک رشته را به عنوان ورودی دریافت میکند و بر اساس `role` یک *boolean* بازمیگراند. انواع roleها در این مدل مشخص اند. شما باید بر اساس جدول زیر به ازای هر رشته ورودی خروجی درست را بازگردانید. توجه کنید که ورودی تابع به صورت [snake_case](https://en.wikipedia.org/wiki/Snake_case) میباشد.
جدول زیر دسترسی های نقش های مختلف در این پروژه را مشخص میکند.
| Action | Guest | Reporter | Developer | Master | Owner |
|:----- |:------:|:--------:|:---------:|:------:|:------------------:|
| create_new_issue | ✓ | ✓ | ✓ | ✓ | ✓ |
| leave_comments | ✓ | ✓ | ✓ | ✓ | ✓ |
| pull_project_code | | ✓ | ✓ | ✓ | ✓ |
| assign_issues_and_merge_requests | | ✓ | ✓ | ✓ | ✓ |
| see_a_list_of_merge_requests | | ✓ | ✓ | ✓ | ✓ |
| manage_merge_requests | | | ✓ | ✓ | ✓ |
| create_new_branches | | | ✓ | ✓ | ✓ |
| add_new_team_members | | | | ✓ | ✓ |
| push_to_protected_branches | | | | ✓ | ✓ |
| switch_visibility_level | | | | | ✓ |
| remove_project | | | | | ✓ |
| force_push_to_protected_branches | | | | | |
### ۳. نوشتن Decorator:
یک فایل به نام `decorators.py` نوشته شده است. شما باید در این فایل یک decorator بنویسید. این decorator در بالای همهی view های موجود در فایل `views.py` قرار داده شده است. این decoratorبه شکل `@projects_panel()` در بالای view ها استفاده میشود که یک آرگومان optional به نام `permissions` دریافت میکند. در صورتی که این آرگومان به decorator پاس داده شود باید به صورت یک لیست باشد. برای مثال:
```python
@projects_panel(permissions=['remove_project'])
def remove_project(request):
request.project.delete()
return redirect('index')
```
در این decorator باید کار های زیر را به ترتیب انجام دهید:
۱. ابتدا باید چک کنید که آیا برای کابر مورد نظر پروژه ای وجود دارد یا خیر.
+ اگر پروژه ای وجود نداشت یک خطای ۴۰۴ با متن `"No projects found"` بازگردانید.
+ در غیر این صورت، `request.memberships` را برابر `queryset` عضویت های کاربر قرار دهید.
۲. سپس باید چک کنید که آیا یک پروژه به عنوان پروژه فعلی برای کاربر وجود دارد یا خیر.
+ اگر پروژه ای به عنوان پروژه فعلی وجود نداشت، عضویت ای با کمترین id را به عنوان عضویت فعلی او انتخاب کنید.
سپس `request.current_membership`را برابر عضویت فعلی کاربر قرار دهید.
۳. `request.project` را برابر پروژه فعلی کاربر قرار دهید.
۴. اگر آرگومان `permissions` وجود داشت باید بررسی کنید که آیا کاربر درعضویت فعلی خود، دسترسی های لازم را دارد یا خیر. برای این کار میتوانید از تابع `has_permission` که در قسمت قبل نوشتید استفاده کنید.
+ اگر دسترسی وجود نداشت باید یک خطای ۴۰۳ (Forbidden) بازگردانید.
# نکات
+ شما تنها مجاز به تغییر در فایل هایی هستید که در ساختار بالا با فلش مشخص شده اند. اگر تغییری در سایر فایلها ایجاد کنید، این تغییرات نادیده گرفته خواهد شد.
+ در فایل `models.py` میتوانید به تعداد دلخواه به هر مدلی تابع اضافه کنید. اما field ها و نام های مدل ها را تغییر ندهید.
+ پس از اعمال تغییرات، کل پروژه را Zip کرده و ارسال کنید.
+ نام فایل Zip اهمیت ندارد.
ارسال پاسخ برای این سؤال
در حال حاضر شما دسترسی ندارید.