File size: 2,759 Bytes
b2e9550
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8b7b048
b2e9550
8b7b048
b2e9550
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Structured abstract section detection and conservative sentence splitting."""
from __future__ import annotations

import re
from dataclasses import dataclass


@dataclass(frozen=True)
class AbstractSentence:
    text: str
    source_location: str
    ordinal: int


_HEADING_TO_LOCATION = {
    "BACKGROUND": "abstract_background",
    "BACKGROUND AND OBJECTIVE": "abstract_background",
    "OBJECTIVE": "abstract_background",
    "OBJECTIVES": "abstract_background",
    "AIM": "abstract_background",
    "AIMS": "abstract_background",
    "INTRODUCTION": "abstract_background",
    "METHOD": "abstract_methods",
    "METHODS": "abstract_methods",
    "MATERIAL AND METHODS": "abstract_methods",
    "MATERIALS AND METHODS": "abstract_methods",
    "DESIGN": "abstract_methods",
    "PARTICIPANTS": "abstract_methods",
    "RESULT": "abstract_results",
    "RESULTS": "abstract_results",
    "FINDINGS": "abstract_results",
    "CONCLUSION": "abstract_conclusion",
    "CONCLUSIONS": "abstract_conclusion",
    "INTERPRETATION": "abstract_conclusion",
}

# INLINE_STRUCTURED_ABSTRACT_HEADINGS_V423
_HEADING_RE = re.compile(
    r"(?im)(?<!\S)(BACKGROUND(?: AND OBJECTIVE)?|OBJECTIVES?|AIMS?|INTRODUCTION|METHODS?|MATERIALS? AND METHODS|DESIGN|PARTICIPANTS|RESULTS?|FINDINGS|CONCLUSIONS?|INTERPRETATION)\s*[:.]\s*"
)

_SENTENCE_RE = re.compile(r"(?<=[.!?])\s+(?=(?:[A-Z0-9]|\())")


def _clean(text: str) -> str:
    return re.sub(r"\s+", " ", text).strip()


def split_sentences(text: str) -> list[str]:
    cleaned = _clean(text)
    if not cleaned:
        return []
    parts = [_clean(part) for part in _SENTENCE_RE.split(cleaned)]
    return [part for part in parts if len(part) >= 10]


def section_abstract(abstract: str) -> list[AbstractSentence]:
    matches = list(_HEADING_RE.finditer(abstract))
    out: list[AbstractSentence] = []
    ordinal = 0

    if not matches:
        for sentence in split_sentences(abstract):
            ordinal += 1
            out.append(AbstractSentence(sentence, "abstract_unspecified", ordinal))
        return out

    prefix = abstract[: matches[0].start()].strip()
    for sentence in split_sentences(prefix):
        ordinal += 1
        out.append(AbstractSentence(sentence, "abstract_unspecified", ordinal))

    for index, match in enumerate(matches):
        heading = match.group(1).upper().strip()
        start = match.end()
        end = matches[index + 1].start() if index + 1 < len(matches) else len(abstract)
        section_text = abstract[start:end]
        location = _HEADING_TO_LOCATION.get(heading, "abstract_unspecified")
        for sentence in split_sentences(section_text):
            ordinal += 1
            out.append(AbstractSentence(sentence, location, ordinal))

    return out