نظرات وبلاگ - پیاده‌سازی


  • محدودیت زمان: ۱ ثانیه
  • محدودیت حافظه: ۲۵۶ مگابایت

در پایین وبلاگ‌های «بله»، کاربرها می‌توانند نظرات خود را بنویسند. در پایین یکی از وبلاگ‌ها تعدادی نظر ثبت شده ولی به دلیل تغییرات شرایط آب‌وهوایی این اطلاعات برای یکی از پست‌ها بهم ریخته است.

می‌دانیم در این پست، nn نظر توسط کاربران ثبت شده است (ترتیب نظرات را نمی‌دانیم). همچنین برای هر نظر می‌دانیم توسط چه کسی و در چه لحظه‌ای ثبت شده، همچنین محتوای آن نظر را می‌دانیم. برخی از نظرات پاسخی به نظرات دیگر است. آن‌ها را باید به نحوی که در نمونه‌ها می‌بینید، با فاصله از سمت چپ صفحه نشان دهید.

فرض کنید عرض صفحه‌ی پیام‌ها برابر LL است. یعنی هر پیام در یک جعبه قرار می‌گیرد که حداکثر LL کاراکتر در آن قرار دارد.

در قسمت بالا سمت چپ نظر، نام فرستنده و پایین سمت راست نظر زمان ارسال نشان داده می‌شود.

حال از شما می‌خواهیم صفحه‌ی نظرات را بازسازی کنید.

نکات جعبه حاوی نظر🔗

سطر اول جعبه

سطر اول حاوی نامِ کاربر است. ابتدا و انتهای این سطر کاراکتر + چاپ کنید. بقیه سطر حاوی LL کاراکتر - است ولی باید بعد از اولین -، نام کاربر را جایگزین -ها کنید. برای مثال اگر L=15L=15 و نام کاربر برابر Amin باشد سطر اول به‌صورت زیر می‌شود.

+-Amin----------+
Plain text
سطرهای میانی جعبه

در سطرهای میانی باید نظر کاربر را بنویسید، ابتدا و انتهای هر سطر که ایجاد می‌کنید باید + باشد و بین دو + به اندازه‌ی LL کاراکتر فاصله باشد. در این فاصله باید پیام کاربر را بنویسید.

اگر بین کلمات چندین فاصله یا space وجود داشت، آن‌ها را یکی در نظر بگیرید و شما به اندازه‌ی یک واحد فاصله دهید. همچنین فاصله یا space‌های اول و آخر متن را در نظر نگیرید.

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

برای مثال اگر L=15L = 15 و متن نظر کاربر برابر عبارت زیر باشد

"      I am     Your User. Can     You Help     Me.  "
Plain text

شما باید به صورت زیر بنویسید

+I am Your User.+
+Can You Help   +
+Me.            +
Plain text
سطر پایانی جعبه

سطر آخر حاوی زمان ارسال نظر است. ابتدا و انتهای این سطر کاراکتر + چاپ کنید. بقیه سطر حاوی LL کاراکتر - است ولی باید قبل از آخرین -، زمان ارسال نظر را جایگزین -ها کنید. برای مثال اگر L=15L=15 و زمان ارسال نظر برابر 00:13:47 باشد سطر آخر به‌صورت زیر می‌شود.

+------00:13:47-+
Plain text

انواع نظرات🔗

نوعsend
send <username> hh:mm:ss "<comment>"
Plain text

به‌جای <username> یک رشته از حروف کوچک یا بزرگ یا ارقام یا ,‍، . یا ; است. طول این رشته حداکثر ۱۰ است.

0hh23,0mm59,0ss590 \leq hh \leq 23, \quad 0 \leq mm \leq 59, \quad 0 \leq ss \leq 59

به‌جای <comment>‌ یک رشته شامل تعدادی کلمه مشابه <username> است و کلمه‌های آن با تعدادی فاصله یا space از هم جدا شده است.

برای مثال

send Amin 00:13:47 "  I am  Your User. Can  You Help  Me.  "
Plain text
نوعreply
reply <username> <index> hh:mm:ss "<comment>"
Plain text

به‌جای <username> یک رشته از حروف کوچک یا بزرگ یا ارقام یا ,‍، . یا ; است. طول این رشته حداکثر ۱۰ است.

به‌جای <index> شماره‌ی سطر نظری را نشان می‌دهد که این نظر به آن جواب داده است. شماره‌ها را از ۱ تا nn به ترتیب ورودی در نظر بگیرید.

0hh23,0mm59,0hh590 \leq hh \leq 23, \quad 0 \leq mm \leq 59, \quad 0 \leq hh \leq 59

به‌جای <comment>‌ یک رشته شامل تعدادی کلمه مشابه <username> است و کلمه‌های آن با تعدادی فاصله یا space از هم جدا شده است.

برای مثال (ریپلای آخر به نظر Amir است.)

send Ali 00:20:00 "second"
send Amir 00:10:00 "first"
send Hossein 00:30:00 "third"
reply Amin 2 00:40:00 "  I am  Your User. Can  You Help  Me?  "
Plain text

نکات مربوط به ریپلای🔗

فاصله نظرات از چپ

اگر نظری پاسخ یک نظر دیگر باشد باید قبل از هر سطر جعبه‌ی آن ۴ کاراکتر < را اضافه کنید. اگر همان نظر پاسخ نظر دیگری باشد باید ۸ کاراکتر < را اضافه کنید و...

برای مثال اگر ورودی به این صورت باشد:

3 15
send A 00:10:00 "Hi"
reply B 1 00:20:00 "Hi"
reply C 2 00:30:00 "Hi"
Plain text

خروجی به صورت زیر است:

+-A-------------+
+Hi             +
+------00:10:00-+
>>>>+-B-------------+
>>>>+Hi             +
>>>>+------00:20:00-+
>>>>>>>>+-C-------------+
>>>>>>>>+Hi             +
>>>>>>>>+------00:30:00-+
Plain text
ترتیب نظرات

شما باید نظرها را به ترتیب زمان ارسال مرتب کنید. همچنین پاسخ هر نظر باید زیر آن نظر باشد. اینکه پاسخ هر نظر باید زیر آن نظر باشد، به ترتیب زمان الویت دارد.

برای مثال اگر ورودی به این صورت باشد:

3 15
send D 00:40:00 "Hi"
send A 00:10:00 "Hi"
send B 00:20:00 "Hi"
reply C 2 00:30:00 "Hi"
Plain text

خروجی به این صورت می‌شود:

+-A-------------+
+Hi             +
+------00:10:00-+
>>>>+-C-------------+
>>>>+Hi             +
>>>>+------00:30:00-+
+-B-------------+
+Hi             +
+------00:20:00-+
+-D-------------+
+Hi             +
+------00:40:00-+
Plain text

زیرمسئله‌ها🔗

زیرمسئله‌ها توضیح ‌ امتیاز
۱ فقط یک پیام را به درستی نشان دهید. ۳۰
۲ چند پیام را به درستی نشان دهید. ۷۵
۳ یک پیام و یک پاسخ به آن را به درستی نشان دهید. ۳۰
۴ چند پیام و چند پاسخ به آن را به درستی نشان دهید. تضمین می‌شود به پاسخ‌ها پاسخی داده نشود. ۷۵
۵ هیچ شرط خاصی نداریم.‌ ۹۰

ورودی🔗

در سطر اول ورودی، دو عدد صحیح nn و LL با فاصله از هم داده می‌شود. nn نشان دهنده‌ی تعداد دستورها و LL عرض صفحه است. 1n100,15L301 \leq n \leq 100, \quad \quad 15 \leq L \leq 30

در nn سطر بعدی در هر سطر یک دستور مطابق با ساختاری که در بالا گفته شده، داده می‌شود.

ورودی‌ها لزوماً به ترتیب زمان نیستند ولی تضمین می‌شود ریپلای یک پیام دیرتر از پیام آمده باشد. همچنین هیچ وقت زمان دو پیام برابر نیست.

خروجی🔗

خروجی شما باید دقیقاً مثل خروجی‌های نمونه و طبق خواسته‌ی سوال باشد.

مثال‌ها🔗

ورودی نمونه ۱🔗

4 15
send amin 00:00:18 "that was so good I liked it" 
send mahla 00:10:19 "Is it rated"
reply romina 2 00:10:30 "yes it is rated" 
send arshia 00:00:50 "I love quera contests"
Plain text

خروجی نمونه ۱🔗

+-amin----------+
+that was so    +
+good I liked it+
+------00:00:18-+
+-arshia--------+
+I love quera   +
+contests       +
+------00:00:50-+
+-mahla---------+
+Is it rated    +
+------00:10:19-+
>>>>+-romina--------+
>>>>+yes it is rated+
>>>>+------00:10:30-+
Plain text

ورودی نمونه ۲🔗

3 20
send Amin 00:00:18 "That Was So gOod I Liked It" 
reply Mahla 1 00:10:19 "Is It Rated"
reply Romina 2 00:10:30 "Yes It Is RaTeD" 
Plain text

خروجی نمونه ۲🔗

+-Amin---------------+
+That Was So gOod I  +
+Liked It            +
+-----------00:00:18-+
>>>>+-Mahla--------------+
>>>>+Is It Rated         +
>>>>+-----------00:10:19-+
>>>>>>>>+-Romina-------------+
>>>>>>>>+Yes It Is RaTeD     +
>>>>>>>>+-----------00:10:30-+
Plain text

ورودی نمونه ۳🔗

4 15
reply user2 4 00:30:00 "   u    p   " 
reply user3 4 00:40:00 " u   p    " 
reply user4 1 00:50:00 " u    p   "
send user1 00:10:00 "    u   p     " 
Plain text

خروجی نمونه ۳🔗

+-user1---------+
+u p            +
+------00:10:00-+
>>>>+-user2---------+
>>>>+u p            +
>>>>+------00:30:00-+
>>>>>>>>+-user4---------+
>>>>>>>>+u p            +
>>>>>>>>+------00:50:00-+
>>>>+-user3---------+
>>>>+u p            +
>>>>+------00:40:00-+
Plain text
لینک‌های مفید برای حل سوال

چالش اطلاعات - دیتابیس


کوئری‌های شما باید روی MySQL قابل اجرا باشند. برای چک کردن سینتکس کد SQL خود می‌توانید از این وبسایت کمک بگیرید.

در این سوال بخشی از پایگاه‌داده یک پیام‌رسان در اختیار شما قرار داده شده‌ است، این بخش از پایگاه‌داده شامل اطلاعات حدود ۱۰۰۰ کاربر و ۲۵۰۰ گروه می‌باشد.

جزئیات پایگاه‌داده🔗

داده‌های اولیه برای تست نهایی را از این لینک دانلود کنید.

توضیحات در مورد داده‌های اولیه

در فایل Messenger.zip فایلی به اسم initial.sql وجود دارد.

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

توضیحات جداول پایگاه‌داده

ساختار جداول به‌شرح زیر است:

۱.جدول users: از این جدول برای نگه‌داری اطلاعات کاربران استفاده می‌شود. ساختار این جدول به‌صورت زیر است:

نام ستون نوع تعریف
id INT شناسه کاربر (کلید اصلی)
phone VARCHAR(20) شماره موبایل کاربر (یکتا)
email VARCHAR(256) ایمیل کاربر (یکتا)
name VARCHAR(128) نام و نام‌خانوادگی کاربر
password VARCHAR(128) رمز عبور کاربر (هَش شده)
reported_num int تعداد دفعاتی که کاربر گزارش (report) شده
created_at datetime تاریخ عضویت
updated_at datetime تاریخ به‌روز رسانی حساب
is_suspend VARCHAR(1) آیا کاربر تعلیق شده‌است (مقدار یک) یا خیر (مقدار صفر)

۲.جدول block_list: از این جدول برای نگه‌داری اطلاعات افراد بلاک (مسدود) شده استفاده می‌شود. ساختار این جدول به‌صورت زیر است:

نام ستون نوع تعریف
id INT شناسه‌ی جدول (کلید اصلی)
user_id INT شناسه کاربر بلاک کننده
blocked_user_id INT شناسه کاربر بلاک شده

۳.جدول group_conversations: از این جدول برای نگه‌داری اطلاعات گروه‌ها استفاده می‌شود. ساختار این جدول به‌صورت زیر است:

نام ستون نوع تعریف
id INT شناسه گروه (کلید اصلی)
string_id VARCHAR(20) آدرس (url) گروه (یکتا)
onwer_id INT شناسه کاربر مالک گروه
name VARCHAR(50) نام گروه

۴.جدول group_conversation_users: از این جدول برای نگه‌داری اطلاعات کاربران هر گروه استفاده می‌شود. ساختار این جدول به‌صورت زیر است:

نام ستون نوع تعریف
id INT شناسه جدول (کلید اصلی)
group_id INT شناسه گروه
user_id INT شناسه کاربر حاضر در گروه
is_admin VARCHAR(1) آیا کاربر ادمین گروه هست (مقدار ۱) یا خیر (مقدار ۰)

مطلوبات🔗🔗

کوئری‌هایی بنویسید که مطلوبات خواسته شده در هر قسمت را انجام دهد:

۱. همان‌طور که در توضیحات جداول گفته شده در جدول کاربران (users) ستونی به نام is_suspend وجود دارد که نشان می‌دهد یک کاربر تعلیق شده است یا خیر، اگر به این ستون پایگاه‌داده دقت کنید، متوجه می‌شوید که این مقدار برای تمامی رکورد‌ها صفر است. حال شما باید کوئری بنویسید که این ستون را برای کاربرانی که ۵ بار یا بیشتر گزارش (ستون reported_num) شده‌اند، برابر یک قرار دهد.

۲. لیستی از کاربران و تعداد بلاک شدن آن‌ها توسط کاربران دیگر.

توضیحات مورد مطلوب دوم

نام هر کاربر را در ستونی به نام blocked_user_name و تعداد کاربرانی که این کاربر را بلاک کرده اند را در ستونی با نام block_count نمایش دهید. همچنین توجه داشته باشید خروجی شما باید بر اساس تعداد بلاک‌ها (ستون block_count) به صورت نزولی مرتب شود و در صورتی که این مقدار برابر بود بر اساس نام کاربر به صورت صعودی مرتب شود.

3 سطر اول خروجی شما باید به شکل زیر باشد.

blocked_user_name block_count
Sandra Garcia 8
Bryan Waters 7
Danielle Henry 7

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

توضیحات مورد مطلوب سوم

نام کاربر اول را در ستونی به نام first_user و نام کاربر دوم را در ستونی با نام second_user و نام گروهی که کاربران در آن مشترک هستند را در ستونی با نام mutual_group قرار دهید و به ترتیب ابتدا کابر اول سپس کاربر دوم و در نهایت براساس نام گروه به صورت صعودی مرتب شود.

توجه داشته باشید شناسه کاربر اول باید همیشه از کاربر دوم بیشتر باشد و همچنین اجرای این کوئری ممکن است کمی زمان‌گیر باشد.

3 سطر اول خروجی شما باید به شکل زیر باشد.

first_user second_user mutual_group
Aaron Foster Anna Little Thompson, Sullivan and Turner
Aaron Foster Ashley Scott Anderson-Hill
Aaron Foster Benjamin Porter Carrillo-Parker

۴. پس از اعمال کوئری اول ستون is_suspend دارای مقادیر معنی دار است، حال ساختاری را بر روی پایگاه‌داده خود پیاده کنید که هنگامی که تعداد گزارش شدن یک کاربر (ستون reported_num) به ۵ می‌رسد (به‌روزرسانی می‌شود) کاربر تعلیق شود (مقدار ستون is_suspend یک شود) و بر عکس، اگر کاربری تعداد گزارش شدنش کم‌تر از ۵ شد از تعلیق در بیاید (مقدار ستون is_suspend صفر شود).

توجه

به روش ارسال پاسخ قسمت چهارم توجه داشته باشید. همچنین توجه داشته باشید برای دریافت امتیاز این قسمت باید امتیاز قسمت اول (مطلوب اول همین سوال) را دریافت کرده باشید.

روش ارسال پاسخ🔗

در یک فایل با نام code.sql کد خود را قرار دهید و آن را فشرده (zip ) کنید و در سایت بارگذاری نمایید. کد شما باید به صورت زیر باشد. (نام فایل zip مهم نیست).


-- Section1

   your first query here

-- Section2

   your second query here

-- Section3

   your third query here

-- Section4
   Delimiter $$
   your fourth query here
   $$
SQL

سامانه بلاگ بَله آباد - Python


توجه کنید که سؤال «سامانه بلاگ بَله آباد» را باید با دقیقاً یکی از زبان‌های Python ،PHP ،Golang یا Node.js حل کنید. در صورتی که تمایل دارید سؤال را با Python حل کنید، می‌توانید از طریق این بخش اقدام به حل کنید. خروجی نهایی در در زبان‌های مختلف تفاوتی ندارد و می‌توانید زبان مورد نظر را با توجه به دانش خود انتخاب کنید.


سلیب پس از ترجمه نقشه متوجه شد که گنج جایی در آبادی « بله آباد » است که در پشت کوه‌های جزیره پنهان شده است. او سریعاً حرکت کرد و هنگامی که به پشت کوه‌های « بله آباد » رسید، متوجه شده که مردمان این آبادی، سامانه‌ای شبیه سیستم بلاگ medium ندارند. برای سلیب این موقعیت خوبی‌بود تا به جای جست‌وجوی شبانه روزی به دنبال گنج، از این طریق ثروتی کسب کند و بیخیال گنج بشود. پس سریعا درخواست پیاده‌سازی این سامانه را با مشخصاتی که در ادامه توضیح می‌دهیم را از شما دارد :‌).

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید.

در این سؤال، ما با دو بخش کلی در ارتباط هستیم، بخش کاربران و بخش بلاگ‌ها. در ادامه به بیان جزئیات هر بخش می‌پردازیم.

بخش کاربران

برای این بخش نیاز است تا تعدادی REST API شامل endpoint های زیر باید پیاده‌سازی شود:

آدرس عنوان
GET / بررسی up بودن سرویس
POST /auth/signup/ ثبت‌نام
POST /auth/login/ ورود به حساب کاربری
POST /auth/logout/ خروج از حساب کاربری

در این API هر کاربر باید یک توکن داشته باشد. این توکن برای هر کاربر ثابت است.

پیاده‌سازی endpoint های موردنیاز بخش کاربر🔗

در همه‌ی endpoint ها، پاسخ باید به‌صورت JSON باشد.

اکیداً توصیه می‌گردد برای پیاده‌سازی بخش کاربر از JWT استفاده کنید.

اطلاعات ورودی به‌صورت application/x-www-form-urlencoded به endpoint ها ارسال می‌شوند.

بررسی up بودن سرویس

پاسخ این endpoint باید به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه: {"message":Welcome to Medium API}
ثبت‌نام

سه پارامتر username و password و email به این endpoint ارسال می‌شوند. در صورتی که حداقل یکی از پارامترهای username و password ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

هر دو پارامتر username و password خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["This field may not be blank."],
          "password": ["This field may not be blank."],
          }
    }
    JSON

پارامتر username خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["This field may not be blank."]
          }
    }
    JSON

پارامتر password خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "password": ["This field may not be blank."]
          }
    }
    JSON

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

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["A user with that username already exists."],
          }
    }
    JSON

در غیر این‌صورت، کاربر باید ساخته شود، یک توکن یکتا برایش تولید شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 201
  • بدنه:
    extensionFromNamejson
    {
      "refresh": "[REFRESH_TOKEN]",
      "access": "[ACCESS_TOKEN]",
    }
    JSON
ورود به حساب کاربری

دو پارامتر username و password باید به این endpoint ارسال شوند. در صورتی که حداقل یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {"error": "Invalid credentials"}

اگر نام کاربری یا رمز عبور نادرست باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {"error": "Invalid credentials"}

در غیر این‌صورت، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "refresh": "[REFRESH_TOKEN]",
      "access": "[ACCESS_TOKEN]",
    }
    JSON
خروج از حساب کاربری

تنها پارامتر refresh باید به این endpoint ارسال شود. در صورتی که این پارامتر ارسال نشده باشد یا برابر با رشته‌ی خالی باشد یا حتی مقدار درستی نداشته باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه: {}

در غیر این‌صورت، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 205
  • بدنه: {}
بخش بلاگ

برای این بخش نیاز است تا تعدادی REST API شامل endpoint های زیر باید پیاده‌سازی شود:

آدرس عنوان
GET /blogs/ دریافت تمامی بلاگ‌های ثبت شده
POST /blogs/create/ ایجاد بلاگ جدید
PUT /blogs/<int:pk>/ آپدیت بلاگ
DELETE /blogs/<int:pk>/ حذف بلاگ
GET /blogs/<int:pk>/detail/ مشاهده بلاگ
POST /blogs/<int:pk>/like/ لایک کردن بلاگ
POST /blogs/analytics/ اوضاع کلی حساب نویسنده

پیاده‌سازی endpoint های موردنیاز بخش کاربر🔗

در همه‌ی endpoint ها، پاسخ باید به‌صورت JSON باشد.

اطلاعات ورودی به‌صورت application/x-www-form-urlencoded به endpoint ها ارسال می‌شوند.

دریافت تمامی بلاگ‌های ثبت شده

این endpoint نیازمند authentication نیست.

نیازی به ارسال هیچ پارامتری به این endpoint نیست. و در همه حالات باید جواب برابر با مقدار زیر باشد.

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    [
      {
          "id": "[BLOG_ID]",
          "title": "[BLOG_TITLE]",
          "content": "[BLOG_CONTENT]",
          "views": "[BLOG_VIEWS]",
          "likes": "[BLOG_LIKES]",
      },
      ...
    ]
    JSON

بلاگ‌های بازگشت داده شده باید به ترتیب زمان ثبت بلاگ باشند.

ایجاد بلاگ جدید

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتر title و content باید به این endpoint ارسال شود. در صورتی که یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "title": ["This field may not be blank."],
          "content": ["This field may not be blank."],
          }
    }
    JSON

در غیر این‌صورت، بلاگ باید ثبت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 201
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
آپدیت بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتر title و content باید به این endpoint ارسال شود. در صورتی که یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "title": ["This field may not be blank."],
          "content": ["This field may not be blank."],
          }
    }
    JSON

در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که یوزر در حال ارسال ریکوئست برابر با نویسنده بلاگ نباشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 403
  • بدنه: {}

در غیر این‌صورت، بلاگ باید آپدیت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
حذف بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتری به این endpoint ارسال نمی‌شود. در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که یوزر در حال ارسال ریکوئست برابر با نویسنده بلاگ نباشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 403
  • بدنه: {}

در غیر این‌صورت، بلاگ باید حذف شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 204
  • بدنه: {}
مشاهده بلاگ

این endpoint نیازمند authentication نیست.

نیازی به ارسال هیچ پارامتری به این endpoint نیست. و در همه حالات باید جواب برابر با مقادیر زیر باشد:

در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در غیر این‌صورت، بلاگ باید ثبت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON

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

نکته ریت لیمیت: در اینجا نیاز به پیاده‌سازی ساز و کاری برای پیاده‌سازی ریت لیمیت داریم. می‌خواهیم تا هر device_id و ip بتواند تنها 10 بار هر بلاگ را ببیند تا میزان ویو‌های هر بلاگ شهودی واقعی از میزان دیده شدن بلاگ بدهد. تا 10 ریکوئست برای گرفتن یک بلاگ از یک device_id و ip را بدون مشکل باز گردانید و میزان ویو بلاگ را هم اضافه کنید. به محض عبور از 10 نیاز است تا پاسخ برابر زیر باشد و به میزان ویو بلاگ هیچ عددی اضافه نشود.

  • کد وضعیت: 429
  • بدنه: {}
لایک کردن بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتری به این endpoint ارسال نمی‌شود. در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که توکن Authorization وجود نداشت و در اصل یوزر لاگین نبود، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {}

در غیر این‌صورت، باید یک عدد به لایک‌های بلاگ اضافه شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
اوضاع کلی حساب نویسنده

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

در صورتی که توکن Authorization وجود نداشت و در اصل یوزر لاگین نبود، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {}

در غیر این‌صورت، باید مجموع تمام view و like های تمام بلاگ‌های نویسنده را محسابه کند و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "total_views": "int",
      "total_likes": "int"
    }
    JSON

نکات تکمیلی🔗

نحوه پیاده‌سازی کاملا بر عهده خودتان است اما برای دریافت نمره کامل مسئله نیاز است تا تمامی دیتاهای مسئله را persist کنید. برای این کار در سیستم داوری ما دیتابیس پستگرسی با مشخصات زیر بالاست:

دیتابیس postgres🔗

هاست: medium_postgres پورت: 5432 یوزرنیم: quera پسورد: quera دیتابیس: quera

نصب نیازمندی‌ها و اجرا

برای حل این سؤال در پوشه medium کد برنامه را نوشته و در فایلی به نام entry.sh که توسط sh اجرا می‌شود، باید برنامه‌ی خود را اجرا کنید. توجه کنید که ما در سیستم داوری اسکریپت entry.sh شما را اجرا می‌کنیم و پس از آن به آدرس localhost ریکوئست می‌زنیم.

برای نصب نیازمندی‌های پایتون از requirements.txt استفاده کنید.

  • نیازی به persistent بودن داده‌ها نیست! اما برای دریافت امتیاز کامل مسئله باید دیتاها را persistent کنید.
  • شما مجاز به تغییر یا ارسال docker-compose.ymlو داکرفایل دلخواه نیستید.
  • سرویس شما باید روی پورت 80 آدرس localhost قابل دسترسی باشد.
  • توصیه می‌کنیم در entry.sh خود APIتان را روی 0.0.0.0:80 اجرا کنید.
  • فراموش نکنید که حتما فایل requirements.txt را در دایرکتوری medium بسازید و تمامی نیازمندی‌های پروژه‌تان را درون آن بنویسید.

نحوه ارسال پاسخ🔗

شما می‌توانید تمامی محتوای موجود در پوشه‌ی medium را تغییر دهید و هر فایلی که می‌خواهید اضافه یا کم کنید.

medium
├──  ...  
├── requirements.txt
└── entry.sh
Plain text

توجه کنید که نام فایل کد شما برای سیستم داوری اهمیتی ندارد و این خود شما هستید که در entry.sh از نام آن برای اجرای پروژه استفاده می‌کنید.

در نهایت این پوشه را ZIP کرده و ارسال کنید. توجه کنید که پس از extract کردن فایل ZIP شما، باید پوشه‌ی medium را ببینیم که درون آن فایل‌های پروژه وجود دارد.

سامانه بلاگ بَله آباد - PHP


توجه کنید که سؤال «سامانه بلاگ بَله آباد» را باید با دقیقاً یکی از زبان‌های Python ،PHP ،Golang یا Node.js حل کنید. در صورتی که تمایل دارید سؤال را با PHP حل کنید، می‌توانید از طریق این بخش اقدام به حل کنید. خروجی نهایی در در زبان‌های مختلف تفاوتی ندارد و می‌توانید زبان مورد نظر را با توجه به دانش خود انتخاب کنید.


سلیب پس از ترجمه نقشه متوجه شد که گنج جایی در آبادی « بله آباد » است که در پشت کوه‌های جزیره پنهان شده است. او سریعاً حرکت کرد و هنگامی که به پشت کوه‌های « بله آباد » رسید، متوجه شده که مردمان این آبادی، سامانه‌ای شبیه سیستم بلاگ medium ندارند. برای سلیب این موقعیت خوبی‌بود تا به جای جست‌وجوی شبانه روزی به دنبال گنج، از این طریق ثروتی کسب کند و بیخیال گنج بشود. پس سریعا درخواست پیاده‌سازی این سامانه را با مشخصاتی که در ادامه توضیح می‌دهیم را از شما دارد :‌).

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید.

در این سؤال، ما با دو بخش کلی در ارتباط هستیم، بخش کاربران و بخش بلاگ‌ها. در ادامه به بیان جزئیات هر بخش می‌پردازیم.

بخش کاربران

برای این بخش نیاز است تا تعدادی REST API شامل endpoint های زیر باید پیاده‌سازی شود:

آدرس عنوان
GET / بررسی up بودن سرویس
POST /auth/signup/ ثبت‌نام
POST /auth/login/ ورود به حساب کاربری
POST /auth/logout/ خروج از حساب کاربری

در این API هر کاربر باید یک توکن داشته باشد. این توکن برای هر کاربر ثابت است.

پیاده‌سازی endpoint های موردنیاز بخش کاربر🔗

در همه‌ی endpoint ها، پاسخ باید به‌صورت JSON باشد.

اکیداً توصیه می‌گردد برای پیاده‌سازی بخش کاربر از JWT استفاده کنید.

اطلاعات ورودی به‌صورت application/x-www-form-urlencoded به endpoint ها ارسال می‌شوند.

بررسی up بودن سرویس

پاسخ این endpoint باید به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه: {"message":Welcome to Medium API}
ثبت‌نام

سه پارامتر username و password و email به این endpoint ارسال می‌شوند. در صورتی که حداقل یکی از پارامترهای username و password ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

هر دو پارامتر username و password خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["This field may not be blank."],
          "password": ["This field may not be blank."],
          }
    }
    JSON

پارامتر username خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["This field may not be blank."]
          }
    }
    JSON

پارامتر password خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "password": ["This field may not be blank."]
          }
    }
    JSON

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

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["A user with that username already exists."],
          }
    }
    JSON

در غیر این‌صورت، کاربر باید ساخته شود، یک توکن یکتا برایش تولید شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 201
  • بدنه:
    extensionFromNamejson
    {
      "refresh": "[REFRESH_TOKEN]",
      "access": "[ACCESS_TOKEN]",
    }
    JSON
ورود به حساب کاربری

دو پارامتر username و password باید به این endpoint ارسال شوند. در صورتی که حداقل یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {"error": "Invalid credentials"}

اگر نام کاربری یا رمز عبور نادرست باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {"error": "Invalid credentials"}

در غیر این‌صورت، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "refresh": "[REFRESH_TOKEN]",
      "access": "[ACCESS_TOKEN]",
    }
    JSON
خروج از حساب کاربری

تنها پارامتر refresh باید به این endpoint ارسال شود. در صورتی که این پارامتر ارسال نشده باشد یا برابر با رشته‌ی خالی باشد یا حتی مقدار درستی نداشته باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه: {}

در غیر این‌صورت، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 205
  • بدنه: {}
بخش بلاگ

برای این بخش نیاز است تا تعدادی REST API شامل endpoint های زیر باید پیاده‌سازی شود:

آدرس عنوان
GET /blogs/ دریافت تمامی بلاگ‌های ثبت شده
POST /blogs/create/ ایجاد بلاگ جدید
PUT /blogs/<int:pk>/ آپدیت بلاگ
DELETE /blogs/<int:pk>/ حذف بلاگ
GET /blogs/<int:pk>/detail/ مشاهده بلاگ
POST /blogs/<int:pk>/like/ لایک کردن بلاگ
POST /blogs/analytics/ اوضاع کلی حساب نویسنده

پیاده‌سازی endpoint های موردنیاز بخش کاربر🔗

در همه‌ی endpoint ها، پاسخ باید به‌صورت JSON باشد.

اطلاعات ورودی به‌صورت application/x-www-form-urlencoded به endpoint ها ارسال می‌شوند.

دریافت تمامی بلاگ‌های ثبت شده

این endpoint نیازمند authentication نیست.

نیازی به ارسال هیچ پارامتری به این endpoint نیست. و در همه حالات باید جواب برابر با مقدار زیر باشد.

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    [
      {
          "id": "[BLOG_ID]",
          "title": "[BLOG_TITLE]",
          "content": "[BLOG_CONTENT]",
          "views": "[BLOG_VIEWS]",
          "likes": "[BLOG_LIKES]",
      },
      ...
    ]
    JSON

بلاگ‌های بازگشت داده شده باید به ترتیب زمان ثبت بلاگ باشند.

ایجاد بلاگ جدید

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتر title و content باید به این endpoint ارسال شود. در صورتی که یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "title": ["This field may not be blank."],
          "content": ["This field may not be blank."],
          }
    }
    JSON

در غیر این‌صورت، بلاگ باید ثبت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 201
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
آپدیت بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتر title و content باید به این endpoint ارسال شود. در صورتی که یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "title": ["This field may not be blank."],
          "content": ["This field may not be blank."],
          }
    }
    JSON

در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که یوزر در حال ارسال ریکوئست برابر با نویسنده بلاگ نباشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 403
  • بدنه: {}

در غیر این‌صورت، بلاگ باید آپدیت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
حذف بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتری به این endpoint ارسال نمی‌شود. در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که یوزر در حال ارسال ریکوئست برابر با نویسنده بلاگ نباشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 403
  • بدنه: {}

در غیر این‌صورت، بلاگ باید حذف شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 204
  • بدنه: {}
مشاهده بلاگ

این endpoint نیازمند authentication نیست.

نیازی به ارسال هیچ پارامتری به این endpoint نیست. و در همه حالات باید جواب برابر با مقادیر زیر باشد:

در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در غیر این‌صورت، بلاگ باید ثبت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON

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

نکته ریت لیمیت: در اینجا نیاز به پیاده‌سازی ساز و کاری برای پیاده‌سازی ریت لیمیت داریم. می‌خواهیم تا هر device_id و ip بتواند تنها 10 بار هر بلاگ را ببیند تا میزان ویو‌های هر بلاگ شهودی واقعی از میزان دیده شدن بلاگ بدهد. تا 10 ریکوئست برای گرفتن یک بلاگ از یک device_id و ip را بدون مشکل باز گردانید و میزان ویو بلاگ را هم اضافه کنید. به محض عبور از 10 نیاز است تا پاسخ برابر زیر باشد و به میزان ویو بلاگ هیچ عددی اضافه نشود.

  • کد وضعیت: 429
  • بدنه: {}
لایک کردن بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتری به این endpoint ارسال نمی‌شود. در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که توکن Authorization وجود نداشت و در اصل یوزر لاگین نبود، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {}

در غیر این‌صورت، باید یک عدد به لایک‌های بلاگ اضافه شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
اوضاع کلی حساب نویسنده

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

در صورتی که توکن Authorization وجود نداشت و در اصل یوزر لاگین نبود، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {}

در غیر این‌صورت، باید مجموع تمام view و like های تمام بلاگ‌های نویسنده را محسابه کند و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "total_views": "int",
      "total_likes": "int"
    }
    JSON

نکات تکمیلی🔗

نحوه پیاده‌سازی کاملا بر عهده خودتان است اما برای دریافت نمره کامل مسئله نیاز است تا تمامی دیتاهای مسئله را persist کنید. برای این کار در سیستم داوری ما دیتابیس پستگرسی با مشخصات زیر بالاست:

دیتابیس postgres🔗

هاست: medium_postgres پورت: 5432 یوزرنیم: quera پسورد: quera دیتابیس: quera

نصب نیازمندی‌ها و اجرا

برای حل این سؤال در پوشه medium کد برنامه را نوشته و در فایلی به نام entry.sh که توسط sh اجرا می‌شود، باید برنامه‌ی خود را اجرا کنید. توجه کنید که ما در سیستم داوری اسکریپت entry.sh شما را اجرا می‌کنیم و پس از آن به آدرس localhost ریکوئست می‌زنیم.

برای نصب نیازمندی‌های پی‌اچ‌پی از composer.json استفاده کنید.

  • نیازی به persistent بودن داده‌ها نیست! اما برای دریافت امتیاز کامل مسئله باید دیتاها را persistent کنید.
  • شما مجاز به تغییر یا ارسال docker-compose.ymlو داکرفایل دلخواه نیستید.
  • سرویس شما باید روی پورت 80 آدرس localhost قابل دسترسی باشد.
  • توصیه می‌کنیم در entry.sh خود APIتان را روی 0.0.0.0:80 اجرا کنید.
  • فراموش نکنید که حتما فایل composer.json را در دایرکتوری medium بسازید و تمامی نیازمندی‌های پروژه‌تان را درون آن بنویسید.

نحوه ارسال پاسخ🔗

شما می‌توانید تمامی محتوای موجود در پوشه‌ی medium را تغییر دهید و هر فایلی که می‌خواهید اضافه یا کم کنید.

medium
├──  ...  
├── composer.json
└── entry.sh
Plain text

توجه کنید که نام فایل کد شما برای سیستم داوری اهمیتی ندارد و این خود شما هستید که در entry.sh از نام آن برای اجرای پروژه استفاده می‌کنید.

در نهایت این پوشه را ZIP کرده و ارسال کنید. توجه کنید که پس از extract کردن فایل ZIP شما، باید پوشه‌ی medium را ببینیم که درون آن فایل‌های پروژه وجود دارد.

سامانه بلاگ بَله آباد - Golang


توجه کنید که سؤال «سامانه بلاگ بَله آباد» را باید با دقیقاً یکی از زبان‌های Python ،PHP ،Golang یا Node.js حل کنید. در صورتی که تمایل دارید سؤال را با Golang حل کنید، می‌توانید از طریق این بخش اقدام به حل کنید. خروجی نهایی در در زبان‌های مختلف تفاوتی ندارد و می‌توانید زبان مورد نظر را با توجه به دانش خود انتخاب کنید.


سلیب پس از ترجمه نقشه متوجه شد که گنج جایی در آبادی « بله آباد » است که در پشت کوه‌های جزیره پنهان شده است. او سریعاً حرکت کرد و هنگامی که به پشت کوه‌های « بله آباد » رسید، متوجه شده که مردمان این آبادی، سامانه‌ای شبیه سیستم بلاگ medium ندارند. برای سلیب این موقعیت خوبی‌بود تا به جای جست‌وجوی شبانه روزی به دنبال گنج، از این طریق ثروتی کسب کند و بیخیال گنج بشود. پس سریعا درخواست پیاده‌سازی این سامانه را با مشخصاتی که در ادامه توضیح می‌دهیم را از شما دارد :‌).

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید.

در این سؤال، ما با دو بخش کلی در ارتباط هستیم، بخش کاربران و بخش بلاگ‌ها. در ادامه به بیان جزئیات هر بخش می‌پردازیم.

بخش کاربران

برای این بخش نیاز است تا تعدادی REST API شامل endpoint های زیر باید پیاده‌سازی شود:

آدرس عنوان
GET / بررسی up بودن سرویس
POST /auth/signup/ ثبت‌نام
POST /auth/login/ ورود به حساب کاربری
POST /auth/logout/ خروج از حساب کاربری

در این API هر کاربر باید یک توکن داشته باشد. این توکن برای هر کاربر ثابت است.

پیاده‌سازی endpoint های موردنیاز بخش کاربر🔗

در همه‌ی endpoint ها، پاسخ باید به‌صورت JSON باشد.

اکیداً توصیه می‌گردد برای پیاده‌سازی بخش کاربر از JWT استفاده کنید.

اطلاعات ورودی به‌صورت application/x-www-form-urlencoded به endpoint ها ارسال می‌شوند.

بررسی up بودن سرویس

پاسخ این endpoint باید به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه: {"message":Welcome to Medium API}
ثبت‌نام

سه پارامتر username و password و email به این endpoint ارسال می‌شوند. در صورتی که حداقل یکی از پارامترهای username و password ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

هر دو پارامتر username و password خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["This field may not be blank."],
          "password": ["This field may not be blank."],
          }
    }
    JSON

پارامتر username خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["This field may not be blank."]
          }
    }
    JSON

پارامتر password خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "password": ["This field may not be blank."]
          }
    }
    JSON

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

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["A user with that username already exists."],
          }
    }
    JSON

در غیر این‌صورت، کاربر باید ساخته شود، یک توکن یکتا برایش تولید شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 201
  • بدنه:
    extensionFromNamejson
    {
      "refresh": "[REFRESH_TOKEN]",
      "access": "[ACCESS_TOKEN]",
    }
    JSON
ورود به حساب کاربری

دو پارامتر username و password باید به این endpoint ارسال شوند. در صورتی که حداقل یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {"error": "Invalid credentials"}

اگر نام کاربری یا رمز عبور نادرست باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {"error": "Invalid credentials"}

در غیر این‌صورت، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "refresh": "[REFRESH_TOKEN]",
      "access": "[ACCESS_TOKEN]",
    }
    JSON
خروج از حساب کاربری

تنها پارامتر refresh باید به این endpoint ارسال شود. در صورتی که این پارامتر ارسال نشده باشد یا برابر با رشته‌ی خالی باشد یا حتی مقدار درستی نداشته باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه: {}

در غیر این‌صورت، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 205
  • بدنه: {}
بخش بلاگ

برای این بخش نیاز است تا تعدادی REST API شامل endpoint های زیر باید پیاده‌سازی شود:

آدرس عنوان
GET /blogs/ دریافت تمامی بلاگ‌های ثبت شده
POST /blogs/create/ ایجاد بلاگ جدید
PUT /blogs/<int:pk>/ آپدیت بلاگ
DELETE /blogs/<int:pk>/ حذف بلاگ
GET /blogs/<int:pk>/detail/ مشاهده بلاگ
POST /blogs/<int:pk>/like/ لایک کردن بلاگ
POST /blogs/analytics/ اوضاع کلی حساب نویسنده

پیاده‌سازی endpoint های موردنیاز بخش کاربر🔗

در همه‌ی endpoint ها، پاسخ باید به‌صورت JSON باشد.

اطلاعات ورودی به‌صورت application/x-www-form-urlencoded به endpoint ها ارسال می‌شوند.

دریافت تمامی بلاگ‌های ثبت شده

این endpoint نیازمند authentication نیست.

نیازی به ارسال هیچ پارامتری به این endpoint نیست. و در همه حالات باید جواب برابر با مقدار زیر باشد.

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    [
      {
          "id": "[BLOG_ID]",
          "title": "[BLOG_TITLE]",
          "content": "[BLOG_CONTENT]",
          "views": "[BLOG_VIEWS]",
          "likes": "[BLOG_LIKES]",
      },
      ...
    ]
    JSON

بلاگ‌های بازگشت داده شده باید به ترتیب زمان ثبت بلاگ باشند.

ایجاد بلاگ جدید

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتر title و content باید به این endpoint ارسال شود. در صورتی که یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "title": ["This field may not be blank."],
          "content": ["This field may not be blank."],
          }
    }
    JSON

در غیر این‌صورت، بلاگ باید ثبت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 201
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
آپدیت بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتر title و content باید به این endpoint ارسال شود. در صورتی که یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "title": ["This field may not be blank."],
          "content": ["This field may not be blank."],
          }
    }
    JSON

در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که یوزر در حال ارسال ریکوئست برابر با نویسنده بلاگ نباشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 403
  • بدنه: {}

در غیر این‌صورت، بلاگ باید آپدیت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
حذف بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتری به این endpoint ارسال نمی‌شود. در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که یوزر در حال ارسال ریکوئست برابر با نویسنده بلاگ نباشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 403
  • بدنه: {}

در غیر این‌صورت، بلاگ باید حذف شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 204
  • بدنه: {}
مشاهده بلاگ

این endpoint نیازمند authentication نیست.

نیازی به ارسال هیچ پارامتری به این endpoint نیست. و در همه حالات باید جواب برابر با مقادیر زیر باشد:

در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در غیر این‌صورت، بلاگ باید ثبت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON

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

نکته ریت لیمیت: در اینجا نیاز به پیاده‌سازی ساز و کاری برای پیاده‌سازی ریت لیمیت داریم. می‌خواهیم تا هر device_id و ip بتواند تنها 10 بار هر بلاگ را ببیند تا میزان ویو‌های هر بلاگ شهودی واقعی از میزان دیده شدن بلاگ بدهد. تا 10 ریکوئست برای گرفتن یک بلاگ از یک device_id و ip را بدون مشکل باز گردانید و میزان ویو بلاگ را هم اضافه کنید. به محض عبور از 10 نیاز است تا پاسخ برابر زیر باشد و به میزان ویو بلاگ هیچ عددی اضافه نشود.

  • کد وضعیت: 429
  • بدنه: {}
لایک کردن بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتری به این endpoint ارسال نمی‌شود. در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که توکن Authorization وجود نداشت و در اصل یوزر لاگین نبود، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {}

در غیر این‌صورت، باید یک عدد به لایک‌های بلاگ اضافه شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
اوضاع کلی حساب نویسنده

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

در صورتی که توکن Authorization وجود نداشت و در اصل یوزر لاگین نبود، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {}

در غیر این‌صورت، باید مجموع تمام view و like های تمام بلاگ‌های نویسنده را محسابه کند و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "total_views": "int",
      "total_likes": "int"
    }
    JSON

نکات تکمیلی🔗

نحوه پیاده‌سازی کاملا بر عهده خودتان است اما برای دریافت نمره کامل مسئله نیاز است تا تمامی دیتاهای مسئله را persist کنید. برای این کار در سیستم داوری ما دیتابیس پستگرسی با مشخصات زیر بالاست:

دیتابیس postgres🔗

هاست: medium_postgres پورت: 5432 یوزرنیم: quera پسورد: quera دیتابیس: quera

نصب نیازمندی‌ها و اجرا

برای حل این سؤال در پوشه medium کد برنامه را نوشته و در فایلی به نام entry.sh که توسط sh اجرا می‌شود، باید برنامه‌ی خود را اجرا کنید. توجه کنید که ما در سیستم داوری اسکریپت entry.sh شما را اجرا می‌کنیم و پس از آن به آدرس localhost ریکوئست می‌زنیم.

برای نصب نیازمندی‌های گولنگ از go.mod استفاده کنید.

  • نیازی به persistent بودن داده‌ها نیست! اما برای دریافت امتیاز کامل مسئله باید دیتاها را persistent کنید.
  • شما مجاز به تغییر یا ارسال docker-compose.ymlو داکرفایل دلخواه نیستید.
  • سرویس شما باید روی پورت 80 آدرس localhost قابل دسترسی باشد.
  • توصیه می‌کنیم در entry.sh خود APIتان را روی 0.0.0.0:80 اجرا کنید.
  • فراموش نکنید که حتما فایل go.mod را در دایرکتوری medium بسازید و تمامی نیازمندی‌های پروژه‌تان را درون آن بنویسید.

نحوه ارسال پاسخ🔗

شما می‌توانید تمامی محتوای موجود در پوشه‌ی medium را تغییر دهید و هر فایلی که می‌خواهید اضافه یا کم کنید.

medium
├──  ...  
├── go.mod
└── entry.sh
Plain text

توجه کنید که نام فایل کد شما برای سیستم داوری اهمیتی ندارد و این خود شما هستید که در entry.sh از نام آن برای اجرای پروژه استفاده می‌کنید.

در نهایت این پوشه را ZIP کرده و ارسال کنید. توجه کنید که پس از extract کردن فایل ZIP شما، باید پوشه‌ی medium را ببینیم که درون آن فایل‌های پروژه وجود دارد.

سامانه بلاگ بَله آباد - Node.js


توجه کنید که سؤال «سامانه بلاگ بَله آباد» را باید با دقیقاً یکی از زبان‌های Python ،PHP ،Golang یا Node.js حل کنید. در صورتی که تمایل دارید سؤال را با Node.js حل کنید، می‌توانید از طریق این بخش اقدام به حل کنید. خروجی نهایی در در زبان‌های مختلف تفاوتی ندارد و می‌توانید زبان مورد نظر را با توجه به دانش خود انتخاب کنید.


سلیب پس از ترجمه نقشه متوجه شد که گنج جایی در آبادی « بله آباد » است که در پشت کوه‌های جزیره پنهان شده است. او سریعاً حرکت کرد و هنگامی که به پشت کوه‌های « بله آباد » رسید، متوجه شده که مردمان این آبادی، سامانه‌ای شبیه سیستم بلاگ medium ندارند. برای سلیب این موقعیت خوبی‌بود تا به جای جست‌وجوی شبانه روزی به دنبال گنج، از این طریق ثروتی کسب کند و بیخیال گنج بشود. پس سریعا درخواست پیاده‌سازی این سامانه را با مشخصاتی که در ادامه توضیح می‌دهیم را از شما دارد :‌).

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید.

در این سؤال، ما با دو بخش کلی در ارتباط هستیم، بخش کاربران و بخش بلاگ‌ها. در ادامه به بیان جزئیات هر بخش می‌پردازیم.

بخش کاربران

برای این بخش نیاز است تا تعدادی REST API شامل endpoint های زیر باید پیاده‌سازی شود:

آدرس عنوان
GET / بررسی up بودن سرویس
POST /auth/signup/ ثبت‌نام
POST /auth/login/ ورود به حساب کاربری
POST /auth/logout/ خروج از حساب کاربری

در این API هر کاربر باید یک توکن داشته باشد. این توکن برای هر کاربر ثابت است.

پیاده‌سازی endpoint های موردنیاز بخش کاربر🔗

در همه‌ی endpoint ها، پاسخ باید به‌صورت JSON باشد.

اکیداً توصیه می‌گردد برای پیاده‌سازی بخش کاربر از JWT استفاده کنید.

اطلاعات ورودی به‌صورت application/x-www-form-urlencoded به endpoint ها ارسال می‌شوند.

بررسی up بودن سرویس

پاسخ این endpoint باید به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه: {"message":Welcome to Medium API}
ثبت‌نام

سه پارامتر username و password و email به این endpoint ارسال می‌شوند. در صورتی که حداقل یکی از پارامترهای username و password ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

هر دو پارامتر username و password خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["This field may not be blank."],
          "password": ["This field may not be blank."],
          }
    }
    JSON

پارامتر username خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["This field may not be blank."]
          }
    }
    JSON

پارامتر password خالی:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "password": ["This field may not be blank."]
          }
    }
    JSON

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

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "username": ["A user with that username already exists."],
          }
    }
    JSON

در غیر این‌صورت، کاربر باید ساخته شود، یک توکن یکتا برایش تولید شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 201
  • بدنه:
    extensionFromNamejson
    {
      "refresh": "[REFRESH_TOKEN]",
      "access": "[ACCESS_TOKEN]",
    }
    JSON
ورود به حساب کاربری

دو پارامتر username و password باید به این endpoint ارسال شوند. در صورتی که حداقل یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {"error": "Invalid credentials"}

اگر نام کاربری یا رمز عبور نادرست باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {"error": "Invalid credentials"}

در غیر این‌صورت، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "refresh": "[REFRESH_TOKEN]",
      "access": "[ACCESS_TOKEN]",
    }
    JSON
خروج از حساب کاربری

تنها پارامتر refresh باید به این endpoint ارسال شود. در صورتی که این پارامتر ارسال نشده باشد یا برابر با رشته‌ی خالی باشد یا حتی مقدار درستی نداشته باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه: {}

در غیر این‌صورت، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 205
  • بدنه: {}
بخش بلاگ

برای این بخش نیاز است تا تعدادی REST API شامل endpoint های زیر باید پیاده‌سازی شود:

آدرس عنوان
GET /blogs/ دریافت تمامی بلاگ‌های ثبت شده
POST /blogs/create/ ایجاد بلاگ جدید
PUT /blogs/<int:pk>/ آپدیت بلاگ
DELETE /blogs/<int:pk>/ حذف بلاگ
GET /blogs/<int:pk>/detail/ مشاهده بلاگ
POST /blogs/<int:pk>/like/ لایک کردن بلاگ
POST /blogs/analytics/ اوضاع کلی حساب نویسنده

پیاده‌سازی endpoint های موردنیاز بخش کاربر🔗

در همه‌ی endpoint ها، پاسخ باید به‌صورت JSON باشد.

اطلاعات ورودی به‌صورت application/x-www-form-urlencoded به endpoint ها ارسال می‌شوند.

دریافت تمامی بلاگ‌های ثبت شده

این endpoint نیازمند authentication نیست.

نیازی به ارسال هیچ پارامتری به این endpoint نیست. و در همه حالات باید جواب برابر با مقدار زیر باشد.

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    [
      {
          "id": "[BLOG_ID]",
          "title": "[BLOG_TITLE]",
          "content": "[BLOG_CONTENT]",
          "views": "[BLOG_VIEWS]",
          "likes": "[BLOG_LIKES]",
      },
      ...
    ]
    JSON

بلاگ‌های بازگشت داده شده باید به ترتیب زمان ثبت بلاگ باشند.

ایجاد بلاگ جدید

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتر title و content باید به این endpoint ارسال شود. در صورتی که یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "title": ["This field may not be blank."],
          "content": ["This field may not be blank."],
          }
    }
    JSON

در غیر این‌صورت، بلاگ باید ثبت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 201
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
آپدیت بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتر title و content باید به این endpoint ارسال شود. در صورتی که یکی از این پارامترها ارسال نشده باشد یا برابر با رشته‌ی خالی باشد، پاسخ باید به‌صورت زیر باشد:

  • کد وضعیت: 400
  • بدنه:
    extensionFromNamejson
    {
      "error": {
          "title": ["This field may not be blank."],
          "content": ["This field may not be blank."],
          }
    }
    JSON

در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که یوزر در حال ارسال ریکوئست برابر با نویسنده بلاگ نباشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 403
  • بدنه: {}

در غیر این‌صورت، بلاگ باید آپدیت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
حذف بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتری به این endpoint ارسال نمی‌شود. در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که یوزر در حال ارسال ریکوئست برابر با نویسنده بلاگ نباشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 403
  • بدنه: {}

در غیر این‌صورت، بلاگ باید حذف شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 204
  • بدنه: {}
مشاهده بلاگ

این endpoint نیازمند authentication نیست.

نیازی به ارسال هیچ پارامتری به این endpoint نیست. و در همه حالات باید جواب برابر با مقادیر زیر باشد:

در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در غیر این‌صورت، بلاگ باید ثبت شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON

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

نکته ریت لیمیت: در اینجا نیاز به پیاده‌سازی ساز و کاری برای پیاده‌سازی ریت لیمیت داریم. می‌خواهیم تا هر device_id و ip بتواند تنها 10 بار هر بلاگ را ببیند تا میزان ویو‌های هر بلاگ شهودی واقعی از میزان دیده شدن بلاگ بدهد. تا 10 ریکوئست برای گرفتن یک بلاگ از یک device_id و ip را بدون مشکل باز گردانید و میزان ویو بلاگ را هم اضافه کنید. به محض عبور از 10 نیاز است تا پاسخ برابر زیر باشد و به میزان ویو بلاگ هیچ عددی اضافه نشود.

  • کد وضعیت: 429
  • بدنه: {}
لایک کردن بلاگ

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

پارامتری به این endpoint ارسال نمی‌شود. در صورتی که pk موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 404
  • بدنه: {}

در صورتی که توکن Authorization وجود نداشت و در اصل یوزر لاگین نبود، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {}

در غیر این‌صورت، باید یک عدد به لایک‌های بلاگ اضافه شود و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "id": "[BLOG_ID]",
      "title": "[BLOG_TITLE]",
      "content": "[BLOG_CONTENT]",
      "views": "[BLOG_VIEWS]",
      "likes": "[BLOG_LIKES]",
    }
    JSON
اوضاع کلی حساب نویسنده

این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer ارسال می‌شود.).

در صورتی که توکن Authorization وجود نداشت و در اصل یوزر لاگین نبود، پاسخ باید به صورت زیر باشد:

  • کد وضعیت: 401
  • بدنه: {}

در غیر این‌صورت، باید مجموع تمام view و like های تمام بلاگ‌های نویسنده را محسابه کند و پاسخ به‌صورت زیر باشد:

  • کد وضعیت: 200
  • بدنه:
    extensionFromNamejson
    {
      "total_views": "int",
      "total_likes": "int"
    }
    JSON

نکات تکمیلی🔗

نحوه پیاده‌سازی کاملا بر عهده خودتان است اما برای دریافت نمره کامل مسئله نیاز است تا تمامی دیتاهای مسئله را persist کنید. برای این کار در سیستم داوری ما دیتابیس پستگرسی با مشخصات زیر بالاست:

دیتابیس postgres🔗

هاست: medium_postgres پورت: 5432 یوزرنیم: quera پسورد: quera دیتابیس: quera

نصب نیازمندی‌ها و اجرا

برای حل این سؤال در پوشه medium کد برنامه را نوشته و در فایلی به نام entry.sh که توسط sh اجرا می‌شود، باید برنامه‌ی خود را اجرا کنید. توجه کنید که ما در سیستم داوری اسکریپت entry.sh شما را اجرا می‌کنیم و پس از آن به آدرس localhost ریکوئست می‌زنیم.

برای نصب نیازمندی‌های نودجی‌اس از package.json استفاده کنید.

  • نیازی به persistent بودن داده‌ها نیست! اما برای دریافت امتیاز کامل مسئله باید دیتاها را persistent کنید.
  • شما مجاز به تغییر یا ارسال docker-compose.ymlو داکرفایل دلخواه نیستید.
  • سرویس شما باید روی پورت 80 آدرس localhost قابل دسترسی باشد.
  • توصیه می‌کنیم در entry.sh خود APIتان را روی 0.0.0.0:80 اجرا کنید.
  • فراموش نکنید که حتما فایل package.json را در دایرکتوری medium بسازید و تمامی نیازمندی‌های پروژه‌تان را درون آن بنویسید.

نحوه ارسال پاسخ🔗

شما می‌توانید تمامی محتوای موجود در پوشه‌ی medium را تغییر دهید و هر فایلی که می‌خواهید اضافه یا کم کنید.

medium
├──  ...  
├── package.json 
└── entry.sh
Plain text

توجه کنید که نام فایل کد شما برای سیستم داوری اهمیتی ندارد و این خود شما هستید که در entry.sh از نام آن برای اجرای پروژه استفاده می‌کنید.

در نهایت این پوشه را ZIP کرده و ارسال کنید. توجه کنید که پس از extract کردن فایل ZIP شما، باید پوشه‌ی medium را ببینیم که درون آن فایل‌های پروژه وجود دارد.