<commit_before><?php

namespace UoBParser;

use \DOMDocument;
use \DOMXpath;
use \DateTime;
use \Exception;

class ParserException extends Exception { }

class Parser
{
    public function __construct($extra = null)
    {
        libxml_use_internal_errors(true);
        $this->extra = $extra != null ? $extra : [];
    }

    private function startTimer()
    {
        $this->startTime = microtime(true);
    }

    /**
     * Generate an array based on either valid output data or an exception
     * @param array/exception $data
     * @return array
     */
    public function makeResponse($data)
    {
        $timeTaken = microtime(true) - $this->startTime;

        $isError = $data instanceof ParserException;

        $outputData = array_merge(['response_time' => $timeTaken, 'error' => $isError], $this->extra);

        if (is_array($data)){
            return array_merge($outputData, $data);
        } else if ($isError) {
            return array_merge($outputData, [
                'error_str' =>  $data->getMessage()
            ]);
        }
    }

    public function getSessions($dept, $course, $level)
    {
        $this->startTimer();

        try {

            //post data to get html string
            $url = 'https://timetable.beds.ac.uk/sws'.Utils::yearString().'/showtimetable.asp';

            //get the current term (estimated)
            //then get relevant lbxWeeks
            $termWeekRanges = [
                0 => range(1, 52),
                1 => range(7, 16),
                2 => range(20, 30),
                3 => range(34, 41)
            ];

            $currentTermWeeks = $termWeekRanges[Utils::estimatedTerm()];

            $params = [
                'ddlDepartments'        =>  $dept,
                'ddlPosGroup'           =>  $level,
                'lbxPos'                =>  $course,
                'lbxWeeks'              =>  implode(';', $currentTermWeeks),
                'ddlWeekdays'           =>  '1-7',
                'ddlPeriods'            =>  '1-34',
                'lstStyle'              =>  'textspreadsheet',
                'btnShowTimetable'      =>  'View Timetable',
                'ObjectClass'           =>  'programme of study',
                'ObjectClassIdentifier' =>  'lbxPos',
                'idtype'                =>  'id'
            ];

            try
            {
                $client = Utils::makeGuzzle();
                $response = $client->request('POST', $url, ['form_params' => $params]);
                $src = $response->getBody();
            } catch (Exception $e) {
                throw new ParserException('Server response error');
            }

            $sessions = $this->parseSessionDocument($src);

            return $this->makeResponse([
                'sessions'  =>  array_map(function($s){ return $s->toArray(); }, $sessions)
            ]);

        } catch (ParserException $e) {
            return $this->makeResponse($e);
        }

    }

    public function parseSessionDocument($src)
    {
        $doc = new DOMDocument();
        $doc->loadHTML($src);

        $xpath = new DOMXpath($doc);
<commit_msg>Added error for incorrect course details<commit_after><?php

namespace UoBParser;

use \DOMDocument;
use \DOMXpath;
use \DateTime;
use \Exception;

class ParserException extends Exception { }

class Parser
{
    public function __construct($extra = null)
    {
        libxml_use_internal_errors(true);
        $this->extra = $extra != null ? $extra : [];
    }

    private function startTimer()
    {
        $this->startTime = microtime(true);
    }

    /**
     * Generate an array based on either valid output data or an exception
     * @param array/exception $data
     * @return array
     */
    public function makeResponse($data)
    {
        $timeTaken = microtime(true) - $this->startTime;

        $isError = $data instanceof ParserException;

        $outputData = array_merge(['response_time' => $timeTaken, 'error' => $isError], $this->extra);

        if (is_array($data)){
            return array_merge($outputData, $data);
        } else if ($isError) {
            return array_merge($outputData, [
                'error_str' =>  $data->getMessage()
            ]);
        }
    }

    public function getSessions($dept, $course, $level)
    {
        $this->startTimer();

        try {

            //post data to get html string
            $url = 'https://timetable.beds.ac.uk/sws'.Utils::yearString().'/showtimetable.asp';

            //get the current term (estimated)
            //then get relevant lbxWeeks
            $termWeekRanges = [
                0 => range(1, 52),
                1 => range(7, 16),
                2 => range(20, 30),
                3 => range(34, 41)
            ];

            $currentTermWeeks = $termWeekRanges[Utils::estimatedTerm()];

            $params = [
                'ddlDepartments'        =>  $dept,
                'ddlPosGroup'           =>  $level,
                'lbxPos'                =>  $course,
                'lbxWeeks'              =>  implode(';', $currentTermWeeks),
                'ddlWeekdays'           =>  '1-7',
                'ddlPeriods'            =>  '1-34',
                'lstStyle'              =>  'textspreadsheet',
                'btnShowTimetable'      =>  'View Timetable',
                'ObjectClass'           =>  'programme of study',
                'ObjectClassIdentifier' =>  'lbxPos',
                'idtype'                =>  'id'
            ];

            try
            {
                $client = Utils::makeGuzzle();
                $response = $client->request('POST', $url, ['form_params' => $params]);
                $src = $response->getBody();
            } catch (Exception $e) {
                throw new ParserException('Server response error');
            }

            $sessions = $this->parseSessionDocument($src);

            return $this->makeResponse([
                'sessions'  =>  array_map(function($s){ return $s->toArray(); }, $sessions)
            ]);

        } catch (ParserException $e) {
            return $this->makeResponse($e);
        }

    }

    public function parseSessionDocument($src)
    {
        //check for invalid session
        //page returns 200 on error, so check contents
        if (strpos($src, 'No Such Page') !== false)
            throw new ParserException('Invalid course details');

        $doc = new DOMDocument();
        $doc->loadHTML($src);

        $xpath = new DOMXpath($doc);

{'fraction_non_alphanumeric': 0.13285320257431812, 'fraction_numerical': 0.007508427827152927, 'mean_word_length': 1.5808620007908265, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 69, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5415550', 'n_tokens_mistral': 1896, 'n_tokens_neox': 1773, 'n_words': 396}