Skip to content

Instantly share code, notes, and snippets.

@sovietscout
Last active April 27, 2025 11:04
Show Gist options
  • Select an option

  • Save sovietscout/1cb94bbdac038eaf2be3066786ccfb66 to your computer and use it in GitHub Desktop.

Select an option

Save sovietscout/1cb94bbdac038eaf2be3066786ccfb66 to your computer and use it in GitHub Desktop.
Calculates expected CUET scores by comparing student responses to the provisional master answer key

CUET Score Checker

Requirements:

  • Python 3.8+
  • pandas, lxml, requests (install via pip install -r pandas lxml requests)
  • master.csv (provisional answer key for the subject; must be in the same folder)

Usage:

  1. Place your response sheet URL in response_url variable.
  2. Run python cuet-score-checker.py.
  3. Get a summary and a scorecard ([student-name]-scorecard.csv) with detailed results.

Output:

  • Total score, correct/incorrect/unattempted counts
  • Question-wise analysis (QID, chosen option, correct answer, marks)

Note:

  • Replace master.csv with updated keys if needed.
  • Response URLs follow the format: https://cdn3.digialm.com/.../...07E1.html
import requests
import pandas as pd
import numpy as np
from lxml import etree
from typing import Union
def get_responses(url: str) -> Union[str, pd.DataFrame]:
r = requests.get(url)
r.raise_for_status()
tree = etree.HTML(r.content)
name_raw: str = tree.xpath('//div[@class="main-info-pnl"]//td[.="Candidate Name"]/following-sibling::td/text()')[0]
name = name_raw.split()[0].strip().lower()
questions = []
for panel in tree.xpath('//div[@class="question-pnl"]'):
qno_raw = panel.xpath('.//td[@class="bold" and starts-with(text(), "Q.")]/text()')[0].split()[-1]
qno = int(qno_raw.split(".")[1].strip())
menu_tbl = panel.xpath('.//table[@class="menu-tbl"]')[0]
qid = int(menu_tbl.xpath('.//td[.="Question ID :"]/following-sibling::td/text()')[0])
ono_raw = menu_tbl.xpath('.//td[.="Chosen Option :"]/following-sibling::td/text()')[0].strip()
if ono_raw == "--":
ono = None
oid = None
else:
ono = float(ono_raw)
oid = float(menu_tbl.xpath(f'.//td[.="Option {ono_raw} ID :"]/following-sibling::td/text()')[0].strip())
questions.append({
"QNO": qno,
"QID": qid,
"ONO": ono,
"OID": oid,
})
return name, pd.DataFrame(questions)
def check_result(your_df: pd.DataFrame, master_df: pd.DataFrame) -> pd.DataFrame:
df = pd.merge(your_df, master_df, how='left', on='QID')
df['TOID'] = df['TOID'].astype(float)
df['Score'] = np.where(df['OID'].isna(), 0, np.where(df['OID'] == df['TOID'], 4, -1))
print(f'Score: {df["Score"].sum()}')
print(f'Correct: {len(df[df['Score'] == 4]['QNO'].tolist())}, Incorrect: {len(df[df['Score'] == -1]['QNO'].tolist())}, Unattempted: {len(df[df['Score'] == 0]['QNO'].tolist())}')
print(df[df['Score'] == -1]['QNO'].tolist())
return df
if __name__ == '__main__':
response_url = '<your-response-sheet-url>'
master = pd.read_csv('master.csv')
stdn_name, stdn = get_responses(response_url)
stdn_df = check_result(stdn, master)
stdn_df.to_csv(f'{stdn_name}-scorecard.csv', index=False)
QID TOID
7396761131 7396764504
7396761132 7396764508
7396761133 7396764512
7396761134 7396764516
7396761135 7396764519
7396761136 7396764524
7396761137 7396764525
7396761138 7396764529
7396761139 7396764536
7396761140 7396764540
7396761141 7396764543
7396761142 7396764546
7396761143 7396764551
7396761144 7396764554
7396761145 7396764557
7396761146 7396764562
7396761147 7396764565
7396761148 7396764569
7396761149 7396764576
7396761150 7396764578
7396761151 7396764583
7396761152 7396764587
7396761153 7396764592
7396761154 7396764596
7396761155 7396764597
7396761156 7396764602
7396761157 7396764607
7396761158 7396764612
7396761159 7396764615
7396761160 7396764620
7396761161 7396764624
7396761162 7396764628
7396761163 7396764630
7396761164 7396764636
7396761165 7396764639
7396761166 7396764642
7396761167 7396764648
7396761168 7396764649
7396761169 7396764656
7396761170 7396764659
7396761171 7396764664
7396761172 7396764667
7396761173 7396764670
7396761174 7396764676
7396761175 7396764677
7396761176 7396764682
7396761177 7396764687
7396761178 7396764692
7396761179 7396764696
7396761180 7396764699
7396761181 7396764702
7396761182 7396764706
7396761183 7396764709
7396761184 7396764716
7396761185 7396764718
7396761186 7396764724
7396761187 7396764727
7396761188 7396764730
7396761189 7396764734
7396761190 7396764740
7396761191 7396764741
7396761192 7396764748
7396761193 7396764750
7396761194 7396764755
7396761195 7396764759
7396761196 7396764764
7396761197 7396764766
7396761198 7396764772
7396761199 7396764774
7396761200 7396764779
7396761201 7396764784
7396761202 7396764785
7396761203 7396764790
7396761204 7396764794
7396761205 7396764799
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment