File size: 8,166 Bytes
187ebb6
 
 
 
 
 
 
aebe30e
 
 
 
 
 
187ebb6
aebe30e
 
 
 
 
 
 
 
 
 
 
 
 
 
379eaa8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aebe30e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187ebb6
aebe30e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187ebb6
 
 
 
 
 
 
 
 
 
 
 
aebe30e
 
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
from bs4 import BeautifulSoup as bs, NavigableString
import requests
from urllib.parse import urljoin
import json
from datetime import datetime
import os
import argparse # For command-line arguments

# --- Configuration ---
# If tesseract is not in your PATH, you might need to specify its location
# For example, on Windows:
# pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

CIRCULAR_DATA_FILE = "circular-data.json"

HEADERS = {
    'Host': 'www.epfindia.gov.in',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0',
    'Accept': 'text/html, */*; q=0.01',
    'Accept-Language': 'en-US,en;q=0.5',
    'Accept-Encoding': 'gzip, deflate, br',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'X-Requested-With': 'XMLHttpRequest',
    'Referer': 'https://www.epfindia.gov.in/site_en/Contact_office_wise.php?id=MHEM',
    'Connection': 'keep-alive',
    'Upgrade-Insecure-Requests': '1'
}

def generate_year_params():
    """Dynamically generates financial year parameters up to the current date."""
    current_date = datetime.now()
    current_year = current_date.year
    current_month = current_date.month
    
    # Financial year in India starts in April
    if current_month >= 4:
        latest_start_year = current_year
    else:
        latest_start_year = current_year - 1
        
    params = []
    # Oldest explicitly listed year before 'Old Circulars' is 2009-2010
    for year in range(latest_start_year, 2008, -1):
        params.append(f"yr={year}-{year+1}")
        
    params.append("yr=Old+Circulars")
    return params

YEAR_PARAMS = generate_year_params()

# --- Utility Functions ---
def load_json_file(filepath):
    """Loads a JSON file if it exists, otherwise returns an empty dictionary."""
    if os.path.exists(filepath):
        try:
            with open(filepath, 'r', encoding='utf-8') as f:
                return json.load(f)
        except json.JSONDecodeError:
            print(f"Warning: Could not decode JSON from {filepath}. Starting fresh.")
            return {}
    return {}

def save_json_file(data, filepath):
    """Saves data to a JSON file."""
    with open(filepath, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=4)
    print(f"Data saved to {filepath}")

# --- Main Data Fetching Logic ---
def fetch_circular_metadata():
    """Fetches circular metadata from EPFO website and saves to circular-data.json."""
    parsed_circulars_data = []
    print("Starting to fetch circular metadata...")

    for q_param in YEAR_PARAMS:
        current_page_url = f'https://www.epfindia.gov.in/site_en/get_cir_content.php?{q_param}'
        print(f"  Requesting URL: {current_page_url}")

        try:
            r = requests.get(current_page_url, headers=HEADERS, timeout=20)
            r.raise_for_status()
            soup = bs(r.text, 'html.parser')
            print(f"  Status Code: {r.status_code} for {q_param}")

            table_rows = soup.find_all('tr')
            data_rows = table_rows[1:] if table_rows and table_rows[0].find('th') else table_rows

            for row_idx, row in enumerate(data_rows):
                cells = row.find_all('td')
                if len(cells) < 4:
                    # print(f"    Skipping row {row_idx+1} in {q_param} due to insufficient cells ({len(cells)}).")
                    continue

                serial_no = cells[0].get_text(strip=True)
                subject_cell = cells[1]
                title_parts = [content.strip() for content in subject_cell.contents if isinstance(content, NavigableString) and content.strip()]
                title = " ".join(title_parts).split('Circular No.')[0].split('No.')[0].strip() # Basic title cleaning

                circular_no_date_raw = ""
                after_first_br = False
                temp_circular_parts = []
                for content in subject_cell.contents:
                    if content.name == 'br':
                        if not after_first_br:
                            after_first_br = True
                            continue
                        else: # Second br or end of relevant part
                            break
                    if after_first_br:
                        if isinstance(content, NavigableString):
                            text_content = content.strip()
                            if text_content:
                                temp_circular_parts.append(text_content)
                        elif content.name == 'a' and temp_circular_parts: # Link after some text
                            break
                circular_no_date_raw = " ".join(filter(None, temp_circular_parts))


                circular_no = ""
                date_of_circular = ""
                delimiter_dated = " dated "
                delimiter_date = " date " # some entries use "date" instead of "dated"
                
                actual_delimiter = None
                if delimiter_dated in circular_no_date_raw.lower(): # Check lower case
                    actual_delimiter = delimiter_dated
                elif delimiter_date in circular_no_date_raw.lower():
                    actual_delimiter = delimiter_date

                if actual_delimiter:
                    # Find the actual delimiter with original casing for split
                    delimiter_pos = circular_no_date_raw.lower().find(actual_delimiter)
                    original_delimiter = circular_no_date_raw[delimiter_pos : delimiter_pos + len(actual_delimiter)]
                    
                    parts = circular_no_date_raw.split(original_delimiter, 1)
                    circular_no = parts[0].strip()
                    if len(parts) > 1:
                        date_of_circular = parts[1].strip()
                else:
                    circular_no = circular_no_date_raw.strip()
                
                # Further clean title from circular number if any residue
                if circular_no and title.endswith(circular_no): # simple check
                    title = title[:-len(circular_no)].strip()


                def get_pdf_link(cell, base_url):
                    link_tag = cell.find('a')
                    if link_tag and link_tag.has_attr('href'):
                        relative_link = link_tag['href']
                        return urljoin(base_url, relative_link)
                    return None

                hindi_pdf_link = get_pdf_link(cells[2], current_page_url)
                english_pdf_link = get_pdf_link(cells[3], current_page_url)

                circular_data = {
                    "serial_no": serial_no,
                    "title": title,
                    "circular_no": circular_no,
                    "date": date_of_circular,
                    "hindi_pdf_link": hindi_pdf_link,
                    "english_pdf_link": english_pdf_link
                }
                parsed_circulars_data.append(circular_data)
            print(f"  Successfully processed {q_param}")

        except requests.exceptions.RequestException as e:
            print(f"  Error fetching {q_param}: {e}")
        except Exception as e:
            print(f"  An error occurred during parsing for {q_param}: {e}")

    if parsed_circulars_data:
        save_json_file(parsed_circulars_data, CIRCULAR_DATA_FILE)
    else:
        print("No circular metadata was extracted.")
    print("Finished fetching circular metadata.")


# --- Main Execution ---
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Fetch EPFO circular metadata.")
    parser.add_argument(
        "--action",
        choices=['fetch', 'all'],
        default='fetch',
        help="'fetch' updates circular-data.json. 'all' is kept as a backwards-compatible alias."
    )
    args = parser.parse_args()

    if args.action in {'fetch', 'all'}:
        fetch_circular_metadata()

    print("\nScript finished.")