indicomb2 0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- indicomb2/__init__.py +1 -0
- indicomb2/contribution.py +27 -0
- indicomb2/event.py +52 -0
- indicomb2/indicomb2.py +182 -0
- indicomb2/markdown.py +116 -0
- indicomb2/meetings.py +91 -0
- indicomb2-0.1.dist-info/METADATA +40 -0
- indicomb2-0.1.dist-info/RECORD +11 -0
- indicomb2-0.1.dist-info/WHEEL +5 -0
- indicomb2-0.1.dist-info/entry_points.txt +2 -0
- indicomb2-0.1.dist-info/top_level.txt +1 -0
indicomb2/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class Contribution:
|
|
7
|
+
title: str
|
|
8
|
+
url: str
|
|
9
|
+
speakers: list[dict] | str
|
|
10
|
+
minutes: str
|
|
11
|
+
start_time: str = None # HH:MM:SS
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def from_indico(cls, start_time: str, speakers: list[dict], **kwargs):
|
|
15
|
+
start_time = datetime.datetime.strptime(start_time, "%H:%M:%S").replace(
|
|
16
|
+
tzinfo=datetime.timezone.utc
|
|
17
|
+
)
|
|
18
|
+
speakers = ", ".join(
|
|
19
|
+
[f"{s['first_name']} {s['last_name']}" for s in speakers],
|
|
20
|
+
)
|
|
21
|
+
return cls(speakers=speakers, start_time=start_time, **kwargs)
|
|
22
|
+
|
|
23
|
+
def __str__(self):
|
|
24
|
+
return f" - [{self.title}]({self.url}) - {self.speakers}"
|
|
25
|
+
|
|
26
|
+
def __lt__(self, other):
|
|
27
|
+
return self.start_time < other.start_time
|
indicomb2/event.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from indicomb2.contribution import Contribution
|
|
4
|
+
from indicomb2.markdown import Markdown
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class Event:
|
|
9
|
+
url: str
|
|
10
|
+
date: str
|
|
11
|
+
title: str
|
|
12
|
+
contributions: list[dict] | list[Contribution]
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def from_indico(cls, url: str, date: str, title: str, contributions: list[dict]):
|
|
16
|
+
contributions = sorted(
|
|
17
|
+
[
|
|
18
|
+
Contribution.from_indico(
|
|
19
|
+
title=c["title"],
|
|
20
|
+
url=c["url"],
|
|
21
|
+
speakers=c["speakers"],
|
|
22
|
+
minutes=c.get("note"),
|
|
23
|
+
start_time=c["startDate"]["time"],
|
|
24
|
+
)
|
|
25
|
+
for c in reversed(contributions)
|
|
26
|
+
],
|
|
27
|
+
)
|
|
28
|
+
return cls(url, date, title, contributions)
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def from_json(cls, url: str, date: str, title: str, contributions: list[Contribution]):
|
|
32
|
+
contributions = [Contribution(**c) for c in contributions]
|
|
33
|
+
return cls(url, date, title, contributions)
|
|
34
|
+
|
|
35
|
+
def __lt__(self, other):
|
|
36
|
+
return self.date > other.date
|
|
37
|
+
|
|
38
|
+
def __str__(self):
|
|
39
|
+
md = Markdown()
|
|
40
|
+
date = md.link(self.date, self.url)
|
|
41
|
+
md += md.header(f"{date} - {self.title}", level=4)
|
|
42
|
+
md += "\n".join([str(c) for c in self.contributions])
|
|
43
|
+
return str(md)
|
|
44
|
+
|
|
45
|
+
def minutes(self):
|
|
46
|
+
md = Markdown()
|
|
47
|
+
date = md.link(self.date, self.url)
|
|
48
|
+
md += md.header(f"{date} - {self.title}", level=4)
|
|
49
|
+
for c in self.contributions:
|
|
50
|
+
if c.minutes:
|
|
51
|
+
md += md.admonition("note", str(c)[3:], str(c.minutes["html"]))
|
|
52
|
+
return str(md)
|
indicomb2/indicomb2.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import datetime
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import multiprocessing as mp
|
|
6
|
+
import os
|
|
7
|
+
import urllib
|
|
8
|
+
from dataclasses import asdict, dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import requests
|
|
12
|
+
import yaml
|
|
13
|
+
|
|
14
|
+
from indicomb2.event import Event
|
|
15
|
+
from indicomb2.meetings import Meetings
|
|
16
|
+
|
|
17
|
+
logging.basicConfig(format="%(asctime)s - %(message)s", level=logging.INFO)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BearerAuth(requests.auth.AuthBase):
|
|
21
|
+
def __init__(self, token: str):
|
|
22
|
+
self.token = token
|
|
23
|
+
|
|
24
|
+
def __call__(self, req):
|
|
25
|
+
req.headers["authorization"] = "Bearer " + self.token
|
|
26
|
+
return req
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class Indico:
|
|
31
|
+
category_id: int
|
|
32
|
+
api_token: str = None
|
|
33
|
+
site: str = "https://indico.cern.ch"
|
|
34
|
+
years: int = 1
|
|
35
|
+
|
|
36
|
+
def __post_init__(self):
|
|
37
|
+
if self.api_token is None:
|
|
38
|
+
self.api_token = os.environ.get("INDICO_API_TOKEN", None)
|
|
39
|
+
if self.api_token is None:
|
|
40
|
+
msg = (
|
|
41
|
+
"Please provide an API token or set INDICO_API_TOKEN or INDICO_API_KEY in your env"
|
|
42
|
+
)
|
|
43
|
+
raise ValueError(msg)
|
|
44
|
+
|
|
45
|
+
self.url = f"{self.site}/category/{self.category_id}/"
|
|
46
|
+
today = datetime.datetime.now(tz=datetime.timezone.utc).date()
|
|
47
|
+
start_date = today - datetime.timedelta(days=365 * self.years)
|
|
48
|
+
self.start_date = start_date.strftime("%Y-%m-%d")
|
|
49
|
+
logging.info(
|
|
50
|
+
f"Feteching meetings for {self.category_id} from {self.start_date}",
|
|
51
|
+
)
|
|
52
|
+
self.events = self.get_category(
|
|
53
|
+
category_id=self.category_id,
|
|
54
|
+
start_date=self.start_date,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def request(self, path: str, params: dict):
|
|
58
|
+
items = list(params.items()) if hasattr(params, "items") else list(params)
|
|
59
|
+
items = sorted(items, key=lambda x: x[0].lower())
|
|
60
|
+
url = f"{self.site}{path}?{urllib.parse.urlencode(items)}"
|
|
61
|
+
response = requests.get(url, auth=BearerAuth(self.api_token), timeout=30)
|
|
62
|
+
response.encoding = response.apparent_encoding
|
|
63
|
+
response.raise_for_status()
|
|
64
|
+
return response.json()
|
|
65
|
+
|
|
66
|
+
def get_category(self, category_id: int, start_date: str, end_date: str = "+15d"):
|
|
67
|
+
category_params = {"from": start_date, "to": end_date, "order": "start"}
|
|
68
|
+
category_path = f"/export/categ/{category_id}.json"
|
|
69
|
+
return self.request(category_path, params=category_params)["results"]
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def filter_events(
|
|
73
|
+
results: dict,
|
|
74
|
+
include: list[str] | None = None,
|
|
75
|
+
exclude: list[str] | None = None,
|
|
76
|
+
):
|
|
77
|
+
if include is None:
|
|
78
|
+
include = []
|
|
79
|
+
if exclude is None:
|
|
80
|
+
exclude = []
|
|
81
|
+
include = [x.lower() for x in include]
|
|
82
|
+
exclude = [x.lower() for x in exclude]
|
|
83
|
+
return [
|
|
84
|
+
r
|
|
85
|
+
for r in results
|
|
86
|
+
if any(x in r["title"].lower() for x in include)
|
|
87
|
+
and not any(x in r["title"].lower() for x in exclude)
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
def get_contributions(self, event: dict, params: dict):
|
|
91
|
+
event_path = f"/export/event/{event['id']}.json"
|
|
92
|
+
event = self.request(event_path, params=params)["results"]
|
|
93
|
+
assert len(event) == 1
|
|
94
|
+
event = event[0]
|
|
95
|
+
return Event.from_indico(
|
|
96
|
+
url=event["url"],
|
|
97
|
+
date=event["startDate"]["date"],
|
|
98
|
+
title=event["title"],
|
|
99
|
+
contributions=event["contributions"],
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def get_meetings(
|
|
103
|
+
self,
|
|
104
|
+
include: list[str] | None = None,
|
|
105
|
+
exclude: list[str] | None = None,
|
|
106
|
+
) -> Meetings:
|
|
107
|
+
if exclude is None:
|
|
108
|
+
exclude = []
|
|
109
|
+
exclude += ["cancelled"]
|
|
110
|
+
events = self.filter_events(self.events, include=include, exclude=exclude)
|
|
111
|
+
params = {"detail": "contributions"}
|
|
112
|
+
results = []
|
|
113
|
+
pool = mp.Pool(processes=mp.cpu_count())
|
|
114
|
+
for event in events:
|
|
115
|
+
kwargs = {"event": event, "params": params}
|
|
116
|
+
pool.apply_async(self.get_contributions, kwds=kwargs, callback=results.append)
|
|
117
|
+
pool.close()
|
|
118
|
+
pool.join()
|
|
119
|
+
logging.info(f"Fetched {len(results)} events")
|
|
120
|
+
return Meetings(sorted(results), self.url, self.start_date)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def main(args=None):
|
|
124
|
+
parser = argparse.ArgumentParser()
|
|
125
|
+
parser.add_argument(
|
|
126
|
+
"-c",
|
|
127
|
+
"--config",
|
|
128
|
+
required=True,
|
|
129
|
+
type=str,
|
|
130
|
+
help="Path to the config file",
|
|
131
|
+
)
|
|
132
|
+
args = parser.parse_args(args)
|
|
133
|
+
|
|
134
|
+
# load config
|
|
135
|
+
with Path(args.config).open() as f:
|
|
136
|
+
config = yaml.safe_load(f)
|
|
137
|
+
|
|
138
|
+
# get sub configs
|
|
139
|
+
summary_config = config["meeting_summaries"]
|
|
140
|
+
topical_contributions_config = config["topical_contributions"]
|
|
141
|
+
minutes_config = config["minutes_summary"]
|
|
142
|
+
|
|
143
|
+
# dump meetings to json
|
|
144
|
+
all_meetings = {}
|
|
145
|
+
for entry in config["scrape"]:
|
|
146
|
+
ind = Indico(category_id=entry["category"], years=entry["years"])
|
|
147
|
+
for name, kwargs in entry["groups"].items():
|
|
148
|
+
logging.info(f"Fetching meetings for {name}")
|
|
149
|
+
meetings = ind.get_meetings(**kwargs)
|
|
150
|
+
all_meetings[name] = meetings
|
|
151
|
+
Path("json").mkdir(exist_ok=True)
|
|
152
|
+
fpath = Path(f"json/{name}.json")
|
|
153
|
+
with fpath.open("w") as f:
|
|
154
|
+
meetings_dict = [asdict(m) for m in meetings.meetings]
|
|
155
|
+
json.dump(meetings_dict, f, indent=4, default=str)
|
|
156
|
+
logging.info(f"Saved to {fpath}")
|
|
157
|
+
|
|
158
|
+
# meeting summaries
|
|
159
|
+
for name, kwargs in summary_config.items():
|
|
160
|
+
logging.info(f"Creating {name} summary")
|
|
161
|
+
meetings = all_meetings[name]
|
|
162
|
+
meetings.meeting_list_md(name=name, **kwargs)
|
|
163
|
+
|
|
164
|
+
# add contribution tables
|
|
165
|
+
logging.info("Adding topical contributions")
|
|
166
|
+
for config in topical_contributions_config:
|
|
167
|
+
cats = config.get("categories", list(all_meetings.keys()))
|
|
168
|
+
meetings = sum((all_meetings[cat] for cat in cats[1:]), all_meetings[cats[0]])
|
|
169
|
+
meetings.add_topic(
|
|
170
|
+
target=config["target"],
|
|
171
|
+
include=config["include"],
|
|
172
|
+
exclude=config.get("exclude"),
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# add minutes tables
|
|
176
|
+
for name, config in minutes_config.items():
|
|
177
|
+
logging.info(f"Adding minutes to {config['target']}")
|
|
178
|
+
all_meetings[name].minutes_md(name=name, target=config["target"])
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
if __name__ == "__main__":
|
|
182
|
+
main()
|
indicomb2/markdown.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Markdown:
|
|
5
|
+
"""Class to write markdown for material for mkdocs."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, md: str = "", hide_toc=False) -> None:
|
|
8
|
+
self.md = md
|
|
9
|
+
if hide_toc:
|
|
10
|
+
self.md += "\n".join(["---", "hide:", " - toc", "---"]) # noqa: FLY002
|
|
11
|
+
|
|
12
|
+
def __add__(self, other):
|
|
13
|
+
if not other.startswith("\n") and self.md:
|
|
14
|
+
other = "\n" + other
|
|
15
|
+
self.md += other
|
|
16
|
+
return self
|
|
17
|
+
|
|
18
|
+
def __radd__(self, other):
|
|
19
|
+
if not self.md.startswith("\n\n") and other:
|
|
20
|
+
self.md = "\n\n" + self.md
|
|
21
|
+
return other + str(self)
|
|
22
|
+
|
|
23
|
+
def __str__(self) -> str:
|
|
24
|
+
return self.md
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def indent(text):
|
|
28
|
+
return "\t" + text.replace("\n", "\n\t")
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def header(text, level):
|
|
32
|
+
return "#" * level + " " + text + "\n"
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def paragraph(text):
|
|
36
|
+
return text + "\n\n"
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def code(text, language="", line_numbers=False):
|
|
40
|
+
ln = ""
|
|
41
|
+
if line_numbers:
|
|
42
|
+
ln += ' linenums="1"'
|
|
43
|
+
return "\n\n```" + language + ln + "\n" + text + "\n```\n\n"
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def link(text, url):
|
|
47
|
+
return "[" + text + "](" + url + ")"
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def image(text, url):
|
|
51
|
+
return ""
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def table(data: dict[str, list], format_ints=True):
|
|
55
|
+
assert len({len(row) for row in data.values()}) == 1
|
|
56
|
+
text = "| "
|
|
57
|
+
for header in data:
|
|
58
|
+
text += header + " | "
|
|
59
|
+
text += "\n| "
|
|
60
|
+
for header in data:
|
|
61
|
+
text += "-" * len(header) + " | "
|
|
62
|
+
text += "\n"
|
|
63
|
+
|
|
64
|
+
for row_idx in range(len(next(iter(data.values())))):
|
|
65
|
+
text += "| "
|
|
66
|
+
for col_idx in range(len(data.keys())):
|
|
67
|
+
value = data[list(data.keys())[col_idx]][row_idx]
|
|
68
|
+
if (format_ints and (isinstance(value, str) and value.isdigit())) or isinstance(
|
|
69
|
+
value, int
|
|
70
|
+
):
|
|
71
|
+
value = f"{int(value):,}"
|
|
72
|
+
text += str(value) + " | "
|
|
73
|
+
text += "\n"
|
|
74
|
+
|
|
75
|
+
text += "\n\n"
|
|
76
|
+
return text
|
|
77
|
+
|
|
78
|
+
@staticmethod
|
|
79
|
+
def list(items):
|
|
80
|
+
text = ""
|
|
81
|
+
for item in items:
|
|
82
|
+
text += "- " + item + "\n"
|
|
83
|
+
text += "\n\n"
|
|
84
|
+
return text
|
|
85
|
+
|
|
86
|
+
def admonition(self, style, header, body=None, expanded=False):
|
|
87
|
+
if body is None:
|
|
88
|
+
expanded = False
|
|
89
|
+
md = "\n"
|
|
90
|
+
md += "!!!" if body is None else "???"
|
|
91
|
+
if expanded:
|
|
92
|
+
md += "+"
|
|
93
|
+
md += " "
|
|
94
|
+
md += style + ' "' + header + '"\n\n'
|
|
95
|
+
if body:
|
|
96
|
+
md += self.indent(body) + "\n\n"
|
|
97
|
+
return md
|
|
98
|
+
|
|
99
|
+
def tabs(self, tabs: dict[str, str]):
|
|
100
|
+
text = "\n"
|
|
101
|
+
for header, body in tabs.items():
|
|
102
|
+
text += "=== " + f'"{header}"' + " \n\n"
|
|
103
|
+
text += self.indent(body) + "\n\n"
|
|
104
|
+
return text
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def bold_text(text):
|
|
108
|
+
return f"**{text}**"
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def italic_text(text):
|
|
112
|
+
return f"*{text}*"
|
|
113
|
+
|
|
114
|
+
def to_file(self, fname):
|
|
115
|
+
with Path(fname).open("w") as f:
|
|
116
|
+
f.write(self.md)
|
indicomb2/meetings.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from indicomb2.event import Event
|
|
6
|
+
from indicomb2.markdown import Markdown
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Meetings:
|
|
11
|
+
meetings: list[Event]
|
|
12
|
+
url: str
|
|
13
|
+
start_date: str
|
|
14
|
+
|
|
15
|
+
def __post_init__(self):
|
|
16
|
+
self.meetings = sorted(self.meetings)
|
|
17
|
+
|
|
18
|
+
def __add__(self, other):
|
|
19
|
+
return Meetings(self.meetings + other.meetings, self.url, self.start_date)
|
|
20
|
+
|
|
21
|
+
def meeting_list_md(self, name: str, when: str, target: Path | str):
|
|
22
|
+
target = Path(target)
|
|
23
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
24
|
+
md = Markdown()
|
|
25
|
+
md += md.header(f"{name.capitalize()} Meetings", level=1)
|
|
26
|
+
name = md.link(name, self.url)
|
|
27
|
+
md += f"The {name} meetings take place every {when} CERN time."
|
|
28
|
+
md += "\n\nYou can find a meeting overview below.\n\n"
|
|
29
|
+
md += md.header("Meeting Overview", level=2)
|
|
30
|
+
last_updated = datetime.datetime.now(tz=datetime.timezone.utc).strftime("%Y-%m-%d %H:%M")
|
|
31
|
+
md += f"*last updated: {last_updated}*\n"
|
|
32
|
+
md += "\n\n".join([str(m) for m in self.meetings])
|
|
33
|
+
md += "\n\n"
|
|
34
|
+
md += (
|
|
35
|
+
f"*date cutoff: {self.start_date}, for older meetings please check "
|
|
36
|
+
f"[indico]({self.url})*\n"
|
|
37
|
+
)
|
|
38
|
+
with target.open("w") as f:
|
|
39
|
+
f.write(str(md))
|
|
40
|
+
|
|
41
|
+
def minutes_md(self, name: str, target: Path):
|
|
42
|
+
target = Path(target)
|
|
43
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
44
|
+
md = Markdown()
|
|
45
|
+
md += md.header(f"{name.capitalize()} Meetings Minutes", level=1)
|
|
46
|
+
last_updated = datetime.datetime.now(tz=datetime.timezone.utc).strftime("%Y-%m-%d %H:%M")
|
|
47
|
+
md += f"*last updated: {last_updated}*\n"
|
|
48
|
+
md += "\n\n".join([m.minutes() for m in self.meetings])
|
|
49
|
+
md += "\n\n"
|
|
50
|
+
md += (
|
|
51
|
+
f"*date cutoff: {self.start_date}, for older meetings please check "
|
|
52
|
+
f"[indico]({self.url})*\n"
|
|
53
|
+
)
|
|
54
|
+
md += md.link("indico", self.url)
|
|
55
|
+
with target.open("w") as f:
|
|
56
|
+
f.write(str(md))
|
|
57
|
+
|
|
58
|
+
def add_topic(
|
|
59
|
+
self,
|
|
60
|
+
target: Path,
|
|
61
|
+
include: list[str] | None = None,
|
|
62
|
+
exclude: list[str] | None = None,
|
|
63
|
+
):
|
|
64
|
+
if include is None:
|
|
65
|
+
include = []
|
|
66
|
+
if exclude is None:
|
|
67
|
+
exclude = []
|
|
68
|
+
data = {"Date": [], "Title": [], "Speakers": []}
|
|
69
|
+
for e in self.meetings:
|
|
70
|
+
for c in e.contributions:
|
|
71
|
+
if any(x.lower() in c.title.lower() for x in include) and not any(
|
|
72
|
+
x.lower() in c.title.lower() for x in exclude
|
|
73
|
+
):
|
|
74
|
+
data["Date"].append(f"[{e.date}]({c.url})")
|
|
75
|
+
data["Title"].append(c.title)
|
|
76
|
+
data["Speakers"].append(c.speakers)
|
|
77
|
+
|
|
78
|
+
md = Markdown()
|
|
79
|
+
md += "\n"
|
|
80
|
+
md += md.header("Meeting Contributions", level=2)
|
|
81
|
+
md += md.table(data)
|
|
82
|
+
md += "\n\n"
|
|
83
|
+
md += f"*search terms: {include}*\n"
|
|
84
|
+
md += (
|
|
85
|
+
f"*date cutoff: {self.start_date}, for older meetings please check "
|
|
86
|
+
f"[indico]({self.url})*\n"
|
|
87
|
+
)
|
|
88
|
+
target = Path(target)
|
|
89
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
with target.open("w") as f:
|
|
91
|
+
f.write(str(md))
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: indicomb2
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: Faster indicomb with mkdocs markdown support
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: pyyaml >=6.0.2
|
|
9
|
+
Requires-Dist: requests >=2.32.3
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest >=8.3.3 ; extra == 'dev'
|
|
12
|
+
Requires-Dist: ruff >=0.6.8 ; extra == 'dev'
|
|
13
|
+
|
|
14
|
+
Faster indicomb with mkdocs markdown support.
|
|
15
|
+
An alternative to the original [indicomb](https://gitlab.cern.ch/indicomb/indicomb).
|
|
16
|
+
|
|
17
|
+
This is fast enough to run in the CI as your docs get built (say goodby to your cron jobs!), and comes with mkdocs markdown support.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install indicomb2
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Set up a config, see the [example](https://gitlab.cern.ch/indicomb/indicomb2/-/blob/main/example.yaml)
|
|
27
|
+
|
|
28
|
+
Run:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
indicomb2 -c my_config.yaml
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## More details
|
|
36
|
+
|
|
37
|
+
1. Set up a CERN docs site: https://how-to.docs.cern.ch/
|
|
38
|
+
2. Add an environment variable `INDICO_API_TOKEN` with the token from https://indico.cern.ch/user/tokens/
|
|
39
|
+
3. Setup a config, see the [example](https://gitlab.cern.ch/indicomb/indicomb2/-/blob/main/example.yaml)
|
|
40
|
+
4. Run `indicomb2 -c my_config.yaml`
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
indicomb2/__init__.py,sha256=Lgf9keBX7aG_JEFt6UYSyiFKcm-BYGtmk9ohpQhYpyk,20
|
|
2
|
+
indicomb2/contribution.py,sha256=6V87pgNAObRq_C9pDv-ET_WKT5OmEQIo-9-jI7zCnuE,775
|
|
3
|
+
indicomb2/event.py,sha256=rE8RBv64-cjshoC6ObJMRF94WY1y_4m1BRgK4K-0ZZo,1642
|
|
4
|
+
indicomb2/indicomb2.py,sha256=ndG9dPY8W6l-HwfjNbHXOZHslYL1qZacBv5ELzKwfcQ,6193
|
|
5
|
+
indicomb2/markdown.py,sha256=zSeqG7zpArFY69rLQ-OG7rleDgW59K37IS0aR6G-dI0,3179
|
|
6
|
+
indicomb2/meetings.py,sha256=7Z3cJYwhNTYUG2dVkHEPKufTgSNp7C1BCrRBpv245jY,3281
|
|
7
|
+
indicomb2-0.1.dist-info/METADATA,sha256=kqPJBsvaT3pkQhLqamWkVrWYxvGSQFEdKtN-UgS5fZM,1150
|
|
8
|
+
indicomb2-0.1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
9
|
+
indicomb2-0.1.dist-info/entry_points.txt,sha256=oPgYgF78V8fSyi_FM2MtKSfsV16iP1R6882Nzx5eRXw,55
|
|
10
|
+
indicomb2-0.1.dist-info/top_level.txt,sha256=O5eDzpAdXFOhQsyuc7H7E7QfKzy82og64kSv_leiSBY,10
|
|
11
|
+
indicomb2-0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
indicomb2
|