upsc_quiz-1.0.x-dev/src/Controller/AdminController.php

src/Controller/AdminController.php
<?php

namespace Drupal\upsc_quiz\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Url;
use Drupal\Core\Link;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

/**
 * Controller for UPSC Quiz administrative functionality.
 */
class AdminController extends ControllerBase implements ContainerInjectionInterface {

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

  /**
   * Constructs an AdminController object.
   *
   * @param \Drupal\Core\Database\Connection $database
   *   The database connection.
   */
  public function __construct(Connection $database) {
    $this->database = $database;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('database')
    );
  }

  /**
   * Manage questions page.
   */
  public function manageQuestions() {
    $build = [];

    // Add header with action buttons
    $build['header'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['upsc-quiz-admin-header']],
    ];

    $build['header']['title'] = [
      '#markup' => '<h2>' . $this->t('Manage Quiz Questions') . '</h2>',
    ];

    $build['header']['actions'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['upsc-quiz-admin-actions']],
    ];

    $add_url = Url::fromRoute('upsc_quiz.admin_questions_add');
    $build['header']['actions']['add_question'] = Link::fromTextAndUrl(
      $this->t('Add New Question'),
      $add_url
    )->toRenderable();
    $build['header']['actions']['add_question']['#attributes'] = [
      'class' => ['button', 'button--primary']
    ];

    $import_url = Url::fromRoute('upsc_quiz.admin_import');
    $build['header']['actions']['import_questions'] = Link::fromTextAndUrl(
      $this->t('Import Questions'),
      $import_url
    )->toRenderable();
    $build['header']['actions']['import_questions']['#attributes'] = [
      'class' => ['button', 'button--secondary']
    ];

    // Questions table
    $build['questions_table'] = [
      '#type' => 'table',
      '#header' => [
        $this->t('ID'),
        $this->t('Section'),
        $this->t('Question'),
        $this->t('Difficulty'),
        $this->t('Status'),
        $this->t('Created'),
        $this->t('Actions'),
      ],
      '#empty' => $this->t('No questions found.'),
      '#attributes' => ['class' => ['upsc-quiz-questions-table']],
    ];

    // Get questions from database
    $query = $this->database->select('upsc_quiz_questions', 'q')
      ->fields('q', ['id', 'section', 'question', 'difficulty', 'status', 'created'])
      ->orderBy('created', 'DESC')
      ->range(0, 50); // Limit to 50 for performance

    $results = $query->execute();

    foreach ($results as $row) {
      $edit_url = Url::fromRoute('upsc_quiz.admin_questions_edit', ['question_id' => $row->id]);
      $delete_url = Url::fromRoute('upsc_quiz.admin_questions_delete', ['question_id' => $row->id]);

      $build['questions_table'][] = [
        'id' => ['#markup' => $row->id],
        'section' => ['#markup' => ucfirst($row->section)],
        'question' => [
          '#markup' => substr($row->question, 0, 100) . (strlen($row->question) > 100 ? '...' : ''),
          '#attributes' => ['class' => ['upsc-quiz-question-text']],
        ],
        'difficulty' => [
          '#markup' => $this->getDifficultyLabel($row->difficulty),
          '#attributes' => ['class' => ['upsc-quiz-difficulty', 'difficulty-' . $row->difficulty]],
        ],
        'status' => [
          '#markup' => $row->status ? $this->t('Published') : $this->t('Unpublished'),
          '#attributes' => ['class' => ['upsc-quiz-status', $row->status ? 'status-published' : 'status-unpublished']],
        ],
        'created' => ['#markup' => date('Y-m-d H:i', $row->created)],
        'actions' => [
          '#type' => 'container',
          '#attributes' => ['class' => ['upsc-quiz-actions']],
          'edit' => Link::fromTextAndUrl($this->t('Edit'), $edit_url)->toRenderable(),
          'delete' => Link::fromTextAndUrl($this->t('Delete'), $delete_url)->toRenderable(),
        ],
      ];
    }

    // Attach CSS
    $build['#attached']['library'][] = 'upsc_quiz/quiz_admin';

    return $build;
  }

  /**
   * Analytics dashboard page.
   */
  public function analytics() {
    $build = [];

    // Page title
    $build['title'] = [
      '#markup' => '<h2>' . $this->t('UPSC Quiz Analytics') . '</h2>',
    ];

    // Summary statistics
    $build['summary'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['upsc-quiz-analytics-summary']],
    ];

    // Get total questions
    $total_questions = $this->database->select('upsc_quiz_questions', 'q')
      ->condition('status', 1)
      ->countQuery()
      ->execute()
      ->fetchField();

    $build['summary']['total_questions'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['analytics-stat']],
      'label' => ['#markup' => '<div class="stat-label">' . $this->t('Total Questions') . '</div>'],
      'value' => ['#markup' => '<div class="stat-value">' . $total_questions . '</div>'],
    ];

    // Get total attempts
    $total_attempts = $this->database->select('upsc_quiz_attempts', 'a')
      ->countQuery()
      ->execute()
      ->fetchField();

    $build['summary']['total_attempts'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['analytics-stat']],
      'label' => ['#markup' => '<div class="stat-label">' . $this->t('Total Attempts') . '</div>'],
      'value' => ['#markup' => '<div class="stat-value">' . $total_attempts . '</div>'],
    ];

    // Get average score
    $avg_score_query = $this->database->select('upsc_quiz_attempts', 'a')
      ->addExpression('AVG(score)', 'avg_score');
    $avg_score = $avg_score_query->execute()->fetchField();
    $avg_score = $avg_score ? round($avg_score, 1) : 0;

    $build['summary']['avg_score'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['analytics-stat']],
      'label' => ['#markup' => '<div class="stat-label">' . $this->t('Average Score') . '</div>'],
      'value' => ['#markup' => '<div class="stat-value">' . $avg_score . '%</div>'],
    ];

    // Section-wise analytics
    $build['section_analytics'] = [
      '#type' => 'table',
      '#header' => [
        $this->t('Section'),
        $this->t('Questions'),
        $this->t('Attempts'),
        $this->t('Avg Score'),
        $this->t('Success Rate'),
      ],
      '#attributes' => ['class' => ['upsc-quiz-analytics-table']],
    ];

    $sections = ['polity', 'history', 'geography', 'economics', 'general_science', 'current_affairs'];

    foreach ($sections as $section) {
      // Questions count
      $questions_count = $this->database->select('upsc_quiz_questions', 'q')
        ->condition('section', $section)
        ->condition('status', 1)
        ->countQuery()
        ->execute()
        ->fetchField();

      // Attempts count
      $attempts_count = $this->database->select('upsc_quiz_attempts', 'a')
        ->condition('section', $section)
        ->countQuery()
        ->execute()
        ->fetchField();

      // Average score
      $section_avg_query = $this->database->select('upsc_quiz_attempts', 'a')
        ->condition('section', $section)
        ->addExpression('AVG(score)', 'avg_score');
      $section_avg = $section_avg_query->execute()->fetchField();
      $section_avg = $section_avg ? round($section_avg, 1) : 0;

      // Success rate (attempts with score >= 60%)
      $success_count = $this->database->select('upsc_quiz_attempts', 'a')
        ->condition('section', $section)
        ->condition('score', 60, '>=')
        ->countQuery()
        ->execute()
        ->fetchField();
        ->countQuery()
        ->execute()
        ->fetchField();

      $success_rate = $attempts_count > 0 ? round(($success_count / $attempts_count) * 100, 1) : 0;

      $build['section_analytics'][] = [
        'section' => ['#markup' => ucfirst(str_replace('_', ' ', $section))],
        'questions' => ['#markup' => $questions_count],
        'attempts' => ['#markup' => $attempts_count],
        'avg_score' => ['#markup' => $section_avg . '%'],
        'success_rate' => ['#markup' => $success_rate . '%'],
      ];
    }

    // Recent attempts table
    $build['recent_attempts'] = [
      '#type' => 'table',
      '#header' => [
        $this->t('User'),
        $this->t('Section'),
        $this->t('Score'),
        $this->t('Time Taken'),
        $this->t('Completed'),
      ],
      '#empty' => $this->t('No recent attempts found.'),
      '#attributes' => ['class' => ['upsc-quiz-recent-attempts']],
    ];

    $recent_attempts = $this->database->select('upsc_quiz_attempts', 'a')
      ->fields('a', ['uid', 'section', 'score', 'time_taken', 'finished'])
      ->orderBy('finished', 'DESC')
      ->range(0, 20)
      ->execute();

    foreach ($recent_attempts as $attempt) {
      $user_name = $attempt->uid ? $this->entityTypeManager()->getStorage('user')->load($attempt->uid)->getDisplayName() : $this->t('Anonymous');

      $build['recent_attempts'][] = [
        'user' => ['#markup' => $user_name],
        'section' => ['#markup' => ucfirst($attempt->section)],
        'score' => ['#markup' => $attempt->score . '%'],
        'time_taken' => ['#markup' => $this->formatTime($attempt->time_taken)],
        'completed' => ['#markup' => date('Y-m-d H:i', $attempt->finished)],
      ];
    }

    // Attach CSS
    $build['#attached']['library'][] = 'upsc_quiz/quiz_admin';

    return $build;
  }

  /**
   * Export quiz data.
   */
  public function exportData() {
    $format = \Drupal::request()->query->get('format', 'csv');

    if ($format === 'csv') {
      return $this->exportCsv();
    }

    // Default to questions export
    return $this->exportQuestionsCsv();
  }

  /**
   * Export questions as CSV.
   */
  private function exportQuestionsCsv() {
    $questions = $this->database->select('upsc_quiz_questions', 'q')
      ->fields('q')
      ->condition('status', 1)
      ->orderBy('section')
      ->orderBy('weight')
      ->execute();

    $csv_data = "ID,Section,Question,Option A,Option B,Option C,Option D,Correct Answer,Explanation,Difficulty,Created\n";

    foreach ($questions as $question) {
      $csv_data .= sprintf(
        "%d,%s,\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",%s,\"%s\",%d,%s\n",
        $question->id,
        $question->section,
        $this->escapeCsv($question->question),
        $this->escapeCsv($question->option_a),
        $this->escapeCsv($question->option_b),
        $this->escapeCsv($question->option_c),
        $this->escapeCsv($question->option_d),
        $question->correct_answer,
        $this->escapeCsv($question->explanation),
        $question->difficulty,
        date('Y-m-d H:i:s', $question->created)
      );
    }

    $response = new Response($csv_data);
    $response->headers->set('Content-Type', 'text/csv');
    $response->headers->set('Content-Disposition', 'attachment; filename="upsc_quiz_questions_' . date('Y-m-d') . '.csv"');

    return $response;
  }

  /**
   * Export results as CSV.
   */
  private function exportCsv() {
    $attempts = $this->database->select('upsc_quiz_attempts', 'a')
      ->fields('a')
      ->orderBy('finished', 'DESC')
      ->execute();

    $csv_data = "ID,User ID,Section,Score,Total Questions,Correct Answers,Time Taken,Completed,Started,Finished\n";

    foreach ($attempts as $attempt) {
      $csv_data .= sprintf(
        "%d,%d,%s,%d,%d,%d,%d,%d,%s,%s\n",
        $attempt->id,
        $attempt->uid,
        $attempt->section,
        $attempt->score,
        $attempt->total_questions,
        $attempt->correct_answers,
        $attempt->time_taken,
        $attempt->completed,
        date('Y-m-d H:i:s', $attempt->started),
        date('Y-m-d H:i:s', $attempt->finished)
      );
    }

    $response = new Response($csv_data);
    $response->headers->set('Content-Type', 'text/csv');
    $response->headers->set('Content-Disposition', 'attachment; filename="upsc_quiz_results_' . date('Y-m-d') . '.csv"');

    return $response;
  }

  /**
   * Get difficulty label.
   */
  private function getDifficultyLabel($level) {
    $labels = [
      1 => $this->t('Easy'),
      2 => $this->t('Medium'),
      3 => $this->t('Hard'),
      4 => $this->t('Very Hard'),
      5 => $this->t('Expert'),
    ];

    return isset($labels[$level]) ? $labels[$level] : $this->t('Unknown');
  }

  /**
   * Format time in human readable format.
   */
  private function formatTime($seconds) {
    $minutes = floor($seconds / 60);
    $remaining_seconds = $seconds % 60;

    if ($minutes > 0) {
      return $this->t('@minutesm @secondss', [
        '@minutes' => $minutes,
        '@seconds' => $remaining_seconds
      ]);
    }

    return $this->t('@secondss', ['@seconds' => $remaining_seconds]);
  }

  /**
   * Escape CSV data.
   */
  private function escapeCsv($data) {
    // Remove newlines and escape quotes
    $data = str_replace(["\r\n", "\n", "\r"], ' ', $data);
    $data = str_replace('"', '""', $data);
    return $data;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc