-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path__main__.py
259 lines (213 loc) · 9.6 KB
/
__main__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
import itertools
import json
import webbrowser
import typing as t
import os
import contextlib
import tempfile
import fitz
import requests
from fitz import TextWriter
#https://papers.gceguide.cc/a-levels/chemistry-(9701)/2023/9701_s23_ms_42.pdf
BASE_MS_LINK = "https://papers.gceguide.cc/a-levels/{}-({})/{}/{}_{}_ms_{}.pdf"
BASE_QP_LINK = "https://papers.gceguide.cc/a-levels/{}-({})/{}/{}_{}_qp_{}.pdf"
with open('subject_info.json') as f:
s_info = json.load(f)
def get_link_info(parsing_string:str) -> dict:
"""It goes through the input and respectively assigns it to aligned position."""
splitted = parsing_string.split('/')
return {
'subject_name':s_info['Subject_Info'][splitted[0]],
'subject_code': splitted[0],
'year':splitted[-1],
'paper':splitted[1],
'season':s_info['Season'][splitted[2].upper()+splitted[3].upper()]
}
def get_link(info:dict, type:str) -> str:
"""Returns the link object which would open the papers."""
s_name = info['subject_name'] if len(info['subject_name'].split()) <= 1 \
else '-'.join(info['subject_name'].split())
return (
BASE_MS_LINK.format(
s_name.lower() ,
info['subject_code'],
'20'+str(info['year']),
info['subject_code'],
info['season']+str(info['year']),
info['paper'],
)
if type == 'ms'
else BASE_QP_LINK.format(
s_name.lower() ,
info['subject_code'],
'20'+str(info['year']),
info['subject_code'],
info['season']+str(info['year']),
info['paper'],
)
)
class Setup_Compilation:
"""Prepares the prerequisites for creating a compilation or downloading past papers in bunch."""
def __init__(self, parsing_string:str):
self.parsing_string = parsing_string
def get_qp(self) -> t.List[dict]:
"""
Returns a list containing dictionary with question paper info to download.
The dictionary is given in terms that would work for `get_link`
"""
parsed = self.parsing_string.split('/')
paper_range = range(int(parsed[1])*10+1, int(parsed[1])*10+4)
season_range = list(s_info['Season'].values())
year_range = list(map(lambda x: int(x), parsed[-1].split('-')))
return [
{
'subject_name': s_info['Subject_Info'][parsed[0]],
'subject_code': parsed[0],
'year': y,
'season': s,
'paper': p,
}
for y, s, p in itertools.product(
range(year_range[0], year_range[1] + 1),
season_range,
paper_range,
)
]
def void_repeated(self) -> list:
"""Avoids downloading repeated papers."""
link_codes = self.get_qp()
for subject_in_repeated in link_codes:
if subject_in_repeated['subject_code'] in s_info['Repeated'].keys() \
and subject_in_repeated['paper'] in s_info['Repeated'][subject_in_repeated['subject_code']]:
link_codes.remove(subject_in_repeated)
return link_codes
def download_qp(self, type_:str) -> None:
"""Downloads the list of question paper extracted from `get_qp`"""
# sourcery skip: avoid-builtin-shadow
link_codes = self.void_repeated()
links = [(f"{link['year']}-{link['season']}-{link['paper']}", get_link(link, type_)) for link in link_codes]
with contextlib.suppress(FileExistsError):
dir = f"papers/{'.'.join(self.parsing_string.split('/'))}"
os.makedirs(dir)
for l in links:
res = requests.get(l[1])
if res.status_code == 200:
with open(f"{dir}/{l[0]}.pdf", 'wb') as f:
f.write(res.content)
class Compilation:
"""Provides a clean and compiled version of the set of papers."""
def __init__(self,files, text, difference):
self.files = files
self.text = text
self.difference = difference - 1
def set_first_page(self):
"""Sets the first page of the compilation."""
doc = fitz.open()
parsed_text = self.text.split('/')
paper_info_text = "".join(s_info['Subject_Info'][parsed_text[0]]) + f" Paper {parsed_text[1]}"
year_info_ = [f'{2000+int(year)}' for year in parsed_text[2].split('-')]
year_info_text = "-".join(year_info_)
page = doc.new_page()
page.draw_rect(page.rect, color=None, fill=(0, 1, 1), overlay=False)
tw = TextWriter(page.rect)
font = fitz.Font("times-bold")
tw.append((76,400), paper_info_text, font=font, fontsize=24)
tw.append((76, 440), year_info_text, font=font, fontsize=20)
tw.write_text(page, color=(0,0,0))
return doc
def set_last_page(self):
"""Sets the last page of the compilation"""
doc = fitz.open()
page = doc.new_page()
page.draw_rect(page.rect, color=None, fill=(0, 1, 1), overlay=False)
return doc
def merge_questions(self):
"""Merges all of the pdf questions into a single file."""
merged_ = fitz.open()
for file in self.files:
doc = fitz.open(file)
merged_.insert_pdf(doc, from_page=self.difference, to_page=len(doc))
return merged_
def create_paper_compilation(self):
doc = fitz.open()
doc.insert_pdf(self.set_first_page())
doc.insert_pdf(self.merge_questions())
doc.insert_pdf(self.set_last_page())
return doc
def open_url(link:str) -> None:
webbrowser.open(link)
def get_yt(parsing_string:str, parsed:dict) -> str:
"""
Returns a search results window in youtube for the question paper.
"""
yt_base = "https://www.youtube.com/results?search_query={}%2F{}%2F{}%2F{}%2F{}"
return yt_base.format(
parsed['subject_code'],
parsed['paper'],
parsing_string.split('/')[2].upper(),
parsing_string.split('/')[3].upper(),
parsed['year']
)
def define_options() -> str:
"""
A function that provies the user with the options present in the software.
"""
return """
This program is designed to help you navigate past papers in a faster and more efficient manner.
You are provided three functions to interact with an individual past paper:
ms - Opens the mark scheme of the past paper.
qp - Opens the question paper of the past paper.
yt - Opens a window searching for the solved video of given past paper.
•Note: The format for all the above function is:
subject_code/paper/season/year, i.e: 9702/42/m/j/22 (Opens physics 42 of 2022)
On top of that you are also provided functions to interact with many different past_papers.
get paper: bulk downloads question papers from the given range
compile paper: combines the bulk download into a single paper
•Note: The format for the above is:
subject_code/paper/year-range, i.e 9702/4/18-21 (Downloads all physics paper 4 from 2018-2021)
"""
count = 0
# sourcery skip: de-morgan
while True:
if count == 0:
print("\n Type 'h' or 'help' for a how to manual.")
count += 1
mode = input("\n Enter your working mode:")
if mode in ['h', 'help']:
print(define_options())
elif mode in ["ms", "qp", "yt"]:
while True:
parse_object = input("Enter the subject code -> ")
if parse_object == 'q':
break
if mode == "yt":
open_url(get_yt(parse_object, get_link_info(parse_object)))
break
open_url(get_link(get_link_info(parse_object), mode))
elif mode in ['get paper', 'compile paper']:
with contextlib.suppress(FileExistsError):
os.mkdir('papers')
while True:
parse_object = input("Enter the subject-code/paper/year-range:")# 9709/1/18-21
if parse_object == 'q':
break
type_ = input("Mark Scheme Or Question Paper? -> ")
if type_.lower() not in ['ms', 'qp']:
break
Setup_Compilation(parse_object).download_qp(type_)
if mode == 'compile paper':
second_parse = input("Enter the difference number of pages from the front page to your first page: ")
if second_parse == 'q':
break
file_path = f"papers/{'.'.join(parse_object.split('/'))}"
file_list = [f"{file_path}/{file}" for file in list(os.walk(file_path))[0][2]]
Compile = Compilation(file_list, parse_object, int(second_parse))
compiled_document = Compile.create_paper_compilation()
compiled_name = f"papers/{s_info['Subject_Info'][parse_object.split('/')[0]].lower()}_p{parse_object.split('/')[1]}_{type_}.pdf"
compiled_document.save(compiled_name)
with contextlib.suppress(FileNotFoundError):
for file in file_list:
os.remove(file)
os.rmdir(file_path)
else:
break