mirror of
https://github.com/migatu/conjurer.git
synced 2026-07-14 21:38:38 +00:00
200 lines
6.3 KiB
Python
200 lines
6.3 KiB
Python
"""
|
|
This module contains functions for searching for DOI (Digital Object Identifier)
|
|
in a list of live results.
|
|
It includes a producer-consumer pattern implementation using threads and queues.
|
|
|
|
Functions:
|
|
- producer: Reads lines from a file and puts them into an output queue.
|
|
- consumer: Consumes items from an input queue and checks if DOI exists in the live results list.
|
|
- search_for_doi: Searches for DOI in live results using the producer-consumer pattern.
|
|
|
|
Global Variables:
|
|
- MAXTHREADS: Maximum number of worker threads.
|
|
- DATABASE_PATH: Path to the database files.
|
|
- ENCODING: Encoding of the database files.
|
|
- CHUNK: File name pattern for the database files.
|
|
- _sentinel: Sentinel object used to signal termination.
|
|
- result_list: List to store the search results.
|
|
- WORK_Q_SIZE: Maximum size of the work queue.
|
|
"""
|
|
|
|
# TODO: Wpiemdolić to wszystko w klasę z loggerem przysłanym z góry
|
|
from queue import Empty, Queue
|
|
from threading import Thread
|
|
import time
|
|
q = Queue()
|
|
|
|
# DATA FOR TEST ONLY
|
|
# MAXTHREADS = 5
|
|
# DATABASE_PATH = r'C:\\Database\\chunks_1\\'
|
|
|
|
# DEPLOYMENT DATA
|
|
MAXTHREADS = 41
|
|
DATABASE_PATH = r"C:\\Database\\chunks\\"
|
|
|
|
ENCODING = "utf-8"
|
|
CHUNK = "_chunk.txt"
|
|
_sentinel = object()
|
|
WORK_Q_SIZE = 35500000
|
|
|
|
|
|
def producer(out_q, control_q, filename, _logger):
|
|
"""
|
|
Produces items from the output queue and puts them into the control queue.
|
|
|
|
Args:
|
|
out_q (Queue): Output queue.
|
|
control_q (Queue): Control queue.
|
|
filename (str): Name of the file.
|
|
_logger: Logger object for logging.
|
|
"""
|
|
with open(DATABASE_PATH + filename, "r+", encoding=ENCODING) as operated_file:
|
|
print(f"Worker {filename} ")
|
|
line_no = 0
|
|
while True:
|
|
line = operated_file.readline()
|
|
line_no += 1
|
|
print(f"\t \t \t \t \t \t W{filename}{line_no}\r", end="")
|
|
|
|
if not line:
|
|
print(f"EOF {filename}")
|
|
break
|
|
|
|
if not line:
|
|
break
|
|
out_q.put(line)
|
|
try:
|
|
check = control_q.get(block=False)
|
|
except Empty:
|
|
check = False
|
|
|
|
if check is _sentinel:
|
|
print("TERM signal received")
|
|
control_q.put(check)
|
|
break
|
|
print(f"Worker finished: {filename}")
|
|
out_q.put(_sentinel)
|
|
|
|
|
|
# IMPORTANT!!! ONLY ONE CONSUMER THREAD AS WE ARE NOT PUTTING SENTINELS BACK
|
|
def consumer(in_q, control_q, doi, live_results, result_list, control_dict, no, _logger):
|
|
"""
|
|
Consumes items from an input queue and checks if DOI exists in the live results list.
|
|
|
|
Args:
|
|
in_q (Queue): Input queue.
|
|
control_q (Queue): Control queue.
|
|
doi (list): List of DOI to search for.
|
|
live_results (list): List to store the search results.
|
|
_logger: Logger object for logging.
|
|
"""
|
|
print(f"Consumer thread started: {no} no")
|
|
empty_counter = 0
|
|
alive_no = 0
|
|
while True:
|
|
done_check = True
|
|
try:
|
|
data = in_q.get(block=True, timeout = 1)
|
|
if data is _sentinel:
|
|
control_dict["sentinels"] += 1
|
|
print(f"Workers finished: {control_dict['sentinels']} reported by consumer {no}")
|
|
|
|
else:
|
|
empty_counter = 0
|
|
alive_no += 1
|
|
print(f"C{no}__{alive_no}\r", end="")
|
|
|
|
for item in result_list:
|
|
if item["DOI"] in data and not item["exists"]:
|
|
print(f"HIT in {no} content {data[0]} line {data[1]} file {data[2]} {item["exists"]}")
|
|
_logger.info(data)
|
|
_logger.info("HIT")
|
|
item["exists"] = True
|
|
live_results.append(item)
|
|
done_check = done_check and item["exists"]
|
|
if done_check:
|
|
control_q.put(_sentinel)
|
|
except Empty:
|
|
empty_counter += 1
|
|
time.sleep(1)
|
|
print(f"Consumer {no} empty")
|
|
if empty_counter > 5:
|
|
print("Consumer %s empty lvl 2", no)
|
|
time.sleep(2)
|
|
elif empty_counter > 10:
|
|
print(f"Consumer thread finished {no}")
|
|
break
|
|
|
|
if control_dict["sentinels"] >= MAXTHREADS:
|
|
_logger.info(f"All workers finished {no}")
|
|
break
|
|
|
|
|
|
|
|
def search_for_doi(doi, live_results, _logger):
|
|
"""
|
|
Search for DOI in live_results using _logger for logging.
|
|
|
|
Args:
|
|
doi (list): List of DOI to search for.
|
|
live_results (list): List to store the search results.
|
|
_logger: Logger object for logging.
|
|
"""
|
|
control_dict = {"sentinels":0}
|
|
result_list = []
|
|
threads = []
|
|
work_q = Queue(maxsize=WORK_Q_SIZE)
|
|
control_q = Queue()
|
|
|
|
for item in doi:
|
|
result_list.append({"DOI": item[0], "exists": False, "data": item[1]})
|
|
|
|
for i in range (0, (len(doi)//1000)+2):
|
|
t_cons = Thread(
|
|
target=consumer, args=(work_q, control_q, doi, live_results, result_list, control_dict, i, _logger)
|
|
)
|
|
_logger.info("Consumer thread created")
|
|
threads.append(t_cons)
|
|
for i in range(0, MAXTHREADS):
|
|
# TEST DATA
|
|
# filename = "test" + str(i) + CHUNk
|
|
# DEPLOYMENT DATA
|
|
filename = str(i) + CHUNK
|
|
_logger.info(f"Creating worker thread no: {i}")
|
|
threads.append(
|
|
Thread(target=producer, args=(work_q, control_q, filename, _logger))
|
|
)
|
|
for worker in threads:
|
|
worker.start()
|
|
for worker in threads:
|
|
worker.join()
|
|
return result_list
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import logging
|
|
|
|
logger = logging.getLogger()
|
|
logger.setLevel("DEBUG")
|
|
h1 = logging.StreamHandler()
|
|
logger.addHandler(h1)
|
|
logger.info("TEST RUN")
|
|
live_result = []
|
|
logger.info(
|
|
search_for_doi(
|
|
[
|
|
("10.1002/9781118786352.wbieg0998.pub2", "DATA"),
|
|
("10.1002/j.2050-0416.2002.tb00563.x", "DATA"),
|
|
("10.1111/j.1365-2958.1994.tb00448.x", "DATA"),
|
|
("10.2165/00128415-200309690-00017", "DATA"),
|
|
("10.5772/48313", "DATA"),
|
|
("10.15803/ijnc.7.2_419", "DATA"),
|
|
("10.1051/0004-6361/201321596e", "DATA"),
|
|
("10.2307/40835941", "DATA"),
|
|
],
|
|
live_result,
|
|
logger,
|
|
)
|
|
)
|
|
logger.info(live_result)
|