gismap 0.2.2__py3-none-any.whl → 0.4.0__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.
@@ -0,0 +1,119 @@
1
+ from string import Template
2
+
3
+
4
+ # language=css
5
+ default_style = Template("""
6
+
7
+ #box-$uid {
8
+ position: relative;
9
+ width: 100% !important;
10
+ height: 80vh !important;
11
+ max-width: 100vw !important;
12
+ max-height: 80vh !important;
13
+ min-height: 80vh;
14
+ color: #111;
15
+ }
16
+
17
+ #vis-$uid {
18
+ height: 100%; /* Make the inner div fill the parent */
19
+ width: 100%; /* Make the inner div fill the parent */
20
+ box-sizing: border-box;
21
+ border: 1px solid #444;
22
+ }
23
+
24
+ html[data-theme="dark"] #vis-$uid {
25
+ background-color: var(--pst-color-background, #14181e);
26
+ }
27
+
28
+ .modal {
29
+ display: none;
30
+ position: fixed;
31
+ z-index: 1000;
32
+ left: 0;
33
+ top: 0;
34
+ width: 100%;
35
+ height: 100%;
36
+ overflow: auto;
37
+ background-color: rgba(10, 10, 10, 0.85);
38
+ }
39
+
40
+ .modal-content {
41
+ background-color: #f4f4f7;
42
+ color: #222235;
43
+ margin: 10% auto;
44
+ padding: 24px;
45
+ border: 1px solid #888;
46
+ width: 50%;
47
+ border-radius: 8px;
48
+ box-shadow: 0 5px 15px rgba(0, 0, 0, .6);
49
+ }
50
+ .modal a {color: #2958d7;}
51
+ .modal a:visited {color: #8435a8;}
52
+
53
+ .close {
54
+ color: #777;
55
+ float: right;
56
+ font-size: 28px;
57
+ font-weight: bold;
58
+ cursor: pointer;
59
+ }
60
+
61
+ .close:hover, .close:focus {
62
+ color: #aaa;
63
+ text-decoration: none;
64
+ cursor: pointer;
65
+ }
66
+
67
+ .watermark {
68
+ position: absolute;
69
+ text-decoration: none;
70
+ color: #888;
71
+ font-size: min(2vw, 10px);
72
+ z-index: 10;
73
+ }
74
+
75
+ .gislink {
76
+ left: 10px;
77
+ bottom: 10px;
78
+ pointer-events: auto;
79
+ }
80
+
81
+ .button {
82
+ background: none;
83
+ border: none;
84
+ padding: 0;
85
+ margin: 0;
86
+ cursor: pointer;
87
+ }
88
+
89
+ .redraw {
90
+ left: 10px;
91
+ top: 10px;
92
+ }
93
+
94
+ .fullscreen {
95
+ bottom: 10px;
96
+ right: 10px;
97
+ }
98
+
99
+ .legend {
100
+ display: inline-block;
101
+ padding: 10px 16px;
102
+ border-radius: 8px;
103
+ box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.10);
104
+ border: 1px solid var(--legend-border, #bbb);
105
+ background: var(--jp-layout-color1, #f5f5fa);
106
+ background-color: var(--legend-bg, rgba(240, 240, 245, 0.95));
107
+ position: absolute;
108
+ top: 12px;
109
+ right: 12px;
110
+ z-index: 20;
111
+ }
112
+
113
+ .legend-entry {
114
+ display: flex;
115
+ margin-right: 10px;
116
+ align-items: center;
117
+ cursor: pointer;
118
+ }
119
+ """)
@@ -0,0 +1,145 @@
1
+ import re
2
+ import unicodedata
3
+ import base64
4
+ from IPython.display import display, HTML
5
+ import ipywidgets as widgets
6
+ from contextlib import contextmanager
7
+
8
+ from gismap.lab.egomap import EgoMap
9
+ from gismap.lab.labmap import ListMap
10
+
11
+
12
+ @contextmanager
13
+ def dummy_context():
14
+ yield
15
+
16
+
17
+ def safe_filename(name):
18
+ """
19
+ Parameters
20
+ ----------
21
+ name: :class:`str`
22
+ Pretty much anything.
23
+
24
+ Returns
25
+ -------
26
+ :class:`str`
27
+ GisMap filename.
28
+ """
29
+ normalized = unicodedata.normalize("NFKD", name)
30
+ ascii_only = normalized.encode("ascii", "ignore").decode("ascii")
31
+ ascii_only = ascii_only.replace(" ", "_")
32
+ safe_str = re.sub(r"[^a-zA-Z0-9_]", "", ascii_only)
33
+ return f"gismap-{safe_str[:60]}.html"
34
+
35
+
36
+ place_holder = "Diego Perino, The-Dang Huynh, François Durand (hal: fradurand, ldb: 38/11269), Rim Kaddah, Leonardo Linguaglossa, Céline Comte"
37
+
38
+
39
+ class GismapWidget:
40
+ """
41
+ A simple widget to test the production of LabMaps and EgoMaps.
42
+
43
+ Examples
44
+ --------
45
+
46
+ This is a doctest example. Use a notebook to play with the widget.
47
+
48
+ >>> gw = GismapWidget() # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
49
+ VBox(children=(HTML(value=''), Output(), HBox(children=(Textarea(value='', ...
50
+ >>> gw.names.value = "Fabien Mathieu"
51
+ >>> gw.dbs.value = "HAL"
52
+ >>> gw.size.value = 3
53
+ >>> gw.compute_function(gw.compute, show=False)
54
+ >>> gw.save_link.value[:30]
55
+ "<a href='data:text/html;base64"
56
+ >>> gw.names.value = "Diego Perino, Laurent Viennot"
57
+ >>> gw.compute_function(gw.compute, show=False)
58
+ >>> gw.save_link.value[:30]
59
+ "<a href='data:text/html;base64"
60
+ """
61
+
62
+ def __init__(self):
63
+ self.names = widgets.Textarea(
64
+ placeholder=place_holder,
65
+ description="Name(s):",
66
+ layout=widgets.Layout(width="50%", height="100px"),
67
+ )
68
+ self.dbs = widgets.RadioButtons(
69
+ options=["HAL", "LDB", "Both"],
70
+ description="DB(s):",
71
+ layout=widgets.Layout(width="80px", max_width="20%"),
72
+ )
73
+ self.size = widgets.IntSlider(
74
+ value=10,
75
+ min=1,
76
+ max=150,
77
+ step=1,
78
+ description="Size",
79
+ layout=widgets.Layout(width="250px"),
80
+ )
81
+ self.compute = widgets.Button(
82
+ description="Map!", layout=widgets.Layout(width="120px", max_width="140px")
83
+ )
84
+ self._col = widgets.VBox(
85
+ [self.size, self.compute],
86
+ layout=widgets.Layout(
87
+ align_items="center", max_width="27%", overflow="hidden"
88
+ ),
89
+ )
90
+ self.save_link = widgets.HTML(value="")
91
+ self.compute.on_click(self.compute_function)
92
+ self.out = widgets.Output()
93
+ self.widget = widgets.VBox(
94
+ [self.save_link, self.out, widgets.HBox([self.names, self._col, self.dbs])]
95
+ )
96
+ display(self.widget)
97
+ self.show = True
98
+
99
+ def html(self):
100
+ dbs = (
101
+ "hal"
102
+ if self.dbs.value == "HAL"
103
+ else "ldb"
104
+ if self.dbs.value == "LDB"
105
+ else ["hal", "ldb"]
106
+ )
107
+ name = self.names.value
108
+ pattern = r",\s*(?![^()]*\))"
109
+ names = [n.strip() for n in re.split(pattern, name)]
110
+ self.save_link.value = ""
111
+ ctx = self.out if self.show else dummy_context()
112
+ if len(names) > 1:
113
+ lab = ListMap(names, dbs=dbs, name="planet")
114
+ if self.show:
115
+ self.out.clear_output()
116
+ with ctx:
117
+ lab.update_authors()
118
+ lab.update_publis()
119
+ extra = self.size.value - len(lab.authors)
120
+ if extra > 0:
121
+ lab.expand(target=extra)
122
+ else:
123
+ lab = EgoMap(names[0], dbs=dbs)
124
+ if self.show:
125
+ self.out.clear_output()
126
+ with ctx:
127
+ lab.build(target=self.size.value)
128
+ return lab.html()
129
+
130
+ def compute_function(self, b, show=True):
131
+ self.show = show
132
+ full = self.html()
133
+ b64 = base64.b64encode(
134
+ f"<html><body>{full}</body></html>".encode("utf8")
135
+ ).decode("utf8")
136
+ payload = f"data:text/html;base64,{b64}"
137
+ savename = safe_filename(self.names.value)
138
+ link_html = (
139
+ f"<a href='{payload}' download='{savename}'>Download the Map!</a>"
140
+ )
141
+ self.save_link.value = link_html
142
+ if show:
143
+ self.out.clear_output()
144
+ with self.out:
145
+ display(HTML(full))
gismap/lab/__init__.py CHANGED
@@ -2,9 +2,5 @@ from gismap.lab.labmap import (
2
2
  LabMap as Map,
3
3
  ListMap as ListMap,
4
4
  )
5
- from gismap.lab.lip6 import Lip6Full as Lip6Full, Lip6Map as Lip6
6
- from gismap.lab.toulouse import SolaceMap as Solace, LaasMap as Laas
7
- from gismap.lab.graph import lab2graph as lab2graph
8
- from gismap.lab.vis import generate_html as generate_html
9
5
  from gismap.lab.egomap import EgoMap as EgoMap
10
6
  from gismap.lab.lab_author import LabAuthor as LabAuthor
gismap/lab/egomap.py CHANGED
@@ -15,15 +15,15 @@ class EgoMap(LabMap):
15
15
 
16
16
  >>> dang = EgoMap("The-Dang Huynh", dbs="hal")
17
17
  >>> dang.build(target=10)
18
- >>> sorted(a.name for a in dang.authors.values()) # doctest: +NORMALIZE_WHITESPACE
19
- ['Bruno Kauffmann', 'Chung Shue Chen', 'Fabien Mathieu', 'François Baccelli', 'Laurent Viennot', 'Ludovic Noirie',
20
- 'Siu-Wai Ho', 'Sébastien Tixeuil', 'The-Dang Huynh', 'Yannick Carlinet']
18
+ >>> sorted(a.name for a in dang.authors.values()) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
19
+ ['Bruno Kauffmann', 'Chung Shue Chen', 'Fabien Mathieu',...
21
20
  """
22
21
 
23
22
  def __init__(self, star, *args, **kwargs):
24
23
  if isinstance(star, str):
25
24
  star = LabAuthor(star)
26
25
  star.metadata.position = (0, 0)
26
+ star.metadata.group = "star"
27
27
  self.star = star
28
28
  super().__init__(*args, **kwargs)
29
29
 
@@ -32,11 +32,10 @@ class EgoMap(LabMap):
32
32
 
33
33
  def build(self, **kwargs):
34
34
  target = kwargs.pop("target", 50)
35
- group = kwargs.pop("group", "moon")
36
35
  self.update_authors(desc="Star metadata")
37
36
  self.update_publis(desc="Star publications")
38
37
  kwargs["target"] = target - len(self.authors)
39
- self.expand(group=None, desc="Planets", **kwargs)
40
- kwargs.update({"target": target - len(self.authors), "group": group})
38
+ self.expand(group="planet", desc="Planets", **kwargs)
39
+ kwargs["target"] = target - len(self.authors)
41
40
  if kwargs["target"] > 0:
42
- self.expand(desc="Moons", **kwargs)
41
+ self.expand(group="moon", desc="Moons", **kwargs)
gismap/lab/expansion.py CHANGED
@@ -73,8 +73,8 @@ def count_prospect_entries(lab):
73
73
  count_publications.append(a.key)
74
74
  else:
75
75
  lab_authors.add(a.key)
76
- for l in lab_authors:
77
- count_coauthors[l].update(new_authors)
76
+ for a in lab_authors:
77
+ count_coauthors[a].update(new_authors)
78
78
 
79
79
  count_coauthors = Counter(
80
80
  k for new_authors in count_coauthors.values() for k in new_authors
@@ -222,12 +222,13 @@ def proper_prospects(
222
222
  locs = [j for j in np.where(jc[i, :] > threshold)[0] if not done[j]]
223
223
  done[locs] = True
224
224
  sources = sort_author_sources([prospects[j].author for j in locs])
225
- strength = sum(prospects[j].score for j in locs)
226
- new_author = LabAuthor.from_sources(sources)
227
- new_lab.append((strength, new_author))
225
+ if sources:
226
+ strength = sum(prospects[j].score for j in locs)
227
+ new_author = LabAuthor.from_sources(sources)
228
+ new_lab.append((strength, new_author))
228
229
 
229
230
  # Extract top prospects
230
- new_lab = [l[1] for l in sorted(new_lab, key=lambda l: l[0], reverse=True)][
231
+ new_lab = [a[1] for a in sorted(new_lab, key=lambda a: a[0], reverse=True)][
231
232
  :max_new
232
233
  ]
233
234
  new_rosetta = {s.key: a for a in new_lab for s in a.sources}
gismap/lab/filters.py CHANGED
@@ -3,7 +3,7 @@ import re
3
3
  # editorials = re.compile(r"ditorial|foreword", re.IGNORECASE)
4
4
  # charlatans = re.compile(r"Raoult|Kofman|Buob")
5
5
 
6
- editorials = ["ditorial", "Foreword", "Brief Announcement"]
6
+ editorials = ["ditorial", "Foreword", "Brief Announcement", "Preface", "Préface"]
7
7
  charlatans = ["Raoult", "Kofman", "Buob"]
8
8
 
9
9
 
gismap/lab/lab_author.py CHANGED
@@ -1,13 +1,14 @@
1
1
  from dataclasses import dataclass, field
2
+ import re
2
3
 
3
- from gismap import get_classes, HAL, DBLP
4
- from gismap.sources.models import DB
4
+ from gismap import get_classes
5
+ from gismap.sources.models import DB, db_class_to_auth_class
5
6
  from gismap.sources.multi import SourcedAuthor, sort_author_sources
6
7
  from gismap.utils.common import LazyRepr, list_of_objects
7
8
  from gismap.utils.logger import logger
8
9
 
9
10
  db_dict = get_classes(DB, key="db_name")
10
- default_dbs = [HAL, DBLP]
11
+ default_dbs = ["hal", "ldb"]
11
12
 
12
13
 
13
14
  @dataclass(repr=False)
@@ -36,6 +37,25 @@ class AuthorMetadata(LazyRepr):
36
37
 
37
38
  @dataclass(repr=False)
38
39
  class LabAuthor(SourcedAuthor):
40
+ """
41
+ Examples
42
+ ---------
43
+ The metadata and DB key(s) of an author can be entered in parentheses using key/values.
44
+
45
+ Improper key/values are ignored (with a warning).
46
+
47
+ >>> dummy= LabAuthor("My Name(img: https://my.url.img, group:me,url:https://mysite.org,hal:key1,ldb:toto,badkey:hello,no_colon_separator)")
48
+ >>> dummy.metadata
49
+ AuthorMetadata(url='https://mysite.org', img='https://my.url.img', group='me')
50
+ >>> dummy.sources
51
+ [HALAuthor(name='My Name', key='key1'), LDBAuthor(name='My Name', key='toto')]
52
+
53
+ You can enter multiple keys for the same DB. HAL key types are automatically detected.
54
+
55
+ >>> dummy2= LabAuthor("My Name (hal:key1,hal:123456,hal: My Other Name )")
56
+ >>> dummy2.sources
57
+ [HALAuthor(name='My Name', key='key1'), HALAuthor(name='My Name', key='123456', key_type='pid'), HALAuthor(name='My Name', key='My Other Name', key_type='fullname')]
58
+ """
39
59
  metadata: AuthorMetadata = field(default_factory=AuthorMetadata)
40
60
 
41
61
  def auto_img(self):
@@ -45,6 +65,30 @@ class LabAuthor(SourcedAuthor):
45
65
  self.metadata.img = img
46
66
  break
47
67
 
68
+ def __post_init__(self):
69
+ pattern = r"\s*([^,(]+)\s*(?:\(([^)]*)\))?\s*$"
70
+ match = re.match(pattern, self.name)
71
+ if match:
72
+ self.name = match.group(1).strip()
73
+ content = match.group(2)
74
+ if content:
75
+ for kv in content.split(","):
76
+ if ":" not in kv:
77
+ logger.warning(f"I don't know what to do with {kv}.")
78
+ continue
79
+ k, v = kv.split(":", 1)
80
+ k = k.strip().lower()
81
+ v = v.strip()
82
+ if k in db_dict:
83
+ DBAuthor = db_class_to_auth_class(db_dict[k])
84
+ self.sources.append(DBAuthor(name=self.name, key=v))
85
+ elif k in ["url", "img", "group"]:
86
+ setattr(self.metadata, k, v)
87
+ else:
88
+ logger.warning(f"I don't know what to do with {kv}.")
89
+ else:
90
+ self.name = self.name.strip()
91
+
48
92
  def auto_sources(self, dbs=None):
49
93
  """
50
94
  Automatically populate the sources based on author's name.
@@ -63,9 +107,9 @@ class LabAuthor(SourcedAuthor):
63
107
  for db in dbs:
64
108
  source = db.search_author(self.name)
65
109
  if len(source) == 0:
66
- logger.warning(f"{self.name} not found in {db.db_name}")
110
+ logger.info(f"{self.name} not found in {db.db_name}")
67
111
  elif len(source) > 1:
68
- logger.warning(f"Multiple entries for {self.name} in {db.db_name}")
112
+ logger.info(f"Multiple entries for {self.name} in {db.db_name}")
69
113
  sources += source
70
114
  if len(sources) > 0:
71
115
  self.sources = sort_author_sources(sources)
@@ -74,7 +118,7 @@ class LabAuthor(SourcedAuthor):
74
118
  def labify_author(author, rosetta):
75
119
  if isinstance(author, LabAuthor):
76
120
  return author
77
- return rosetta.get(author.key, author)
121
+ return rosetta.get(author.key, rosetta.get(author.name, author))
78
122
 
79
123
 
80
124
  def labify_publications(pubs, rosetta):
gismap/lab/labmap.py CHANGED
@@ -23,7 +23,7 @@ from gismap.lab.filters import (
23
23
  publication_size_filter,
24
24
  publication_oneword_filter,
25
25
  )
26
- from gismap.lab.graph import lab2graph
26
+ from gismap.gisgraphs.builder import make_vis
27
27
 
28
28
 
29
29
  class LabMap(MixInIO):
@@ -38,7 +38,7 @@ class LabMap(MixInIO):
38
38
  ----------
39
39
  name: :class:`str`
40
40
  Name of the lab. Can be set as class or instance attribute.
41
- dbs: :class:`list`, default=[:class:`~gismap.sources.hal.HAL`, :class:`~gismap.sources.dblp.DBLP`]
41
+ dbs: :class:`list`, default=[:class:`~gismap.sources.hal.HAL`, :class:`~gismap.sources.ldb.LDB`]
42
42
  List of DB sources to use.
43
43
 
44
44
 
@@ -57,8 +57,7 @@ class LabMap(MixInIO):
57
57
  def __init__(self, name=None, dbs=None):
58
58
  if name is not None:
59
59
  self.name = name
60
- if dbs is not None:
61
- self.dbs = list_of_objects(dbs, db_dict, default=default_dbs)
60
+ self.dbs = dbs
62
61
  self.author_selectors = [author_taboo_filter()]
63
62
  self.publication_selectors = [
64
63
  publication_size_filter(),
@@ -92,11 +91,13 @@ class LabMap(MixInIO):
92
91
  if not all(f(author) for f in self.author_selectors):
93
92
  continue
94
93
  if len(author.sources) == 0:
95
- author.auto_sources(dbs=self.dbs)
94
+ author.auto_sources(dbs=list_of_objects(self.dbs, db_dict, default=default_dbs))
96
95
  if author.sources:
97
96
  self.authors[author.key] = author
98
97
  if author.metadata.img is None:
99
98
  author.auto_img()
99
+ if author.metadata.group is None:
100
+ author.metadata.group = self.name
100
101
 
101
102
  def update_publis(self, desc="Publications information"):
102
103
  """
@@ -151,7 +152,7 @@ class LabMap(MixInIO):
151
152
  return None
152
153
 
153
154
  def html(self, **kwargs):
154
- return lab2graph(self, **kwargs)
155
+ return make_vis(self, **kwargs)
155
156
 
156
157
  def save_html(self, name=None, **kwargs):
157
158
  if name is None:
File without changes
@@ -0,0 +1,46 @@
1
+ import requests
2
+ from bs4 import BeautifulSoup as Soup
3
+ from gismap.lab.labmap import LabMap
4
+ from gismap.lab.lab_author import AuthorMetadata, LabAuthor
5
+ from gismap.utils.requests import get
6
+
7
+
8
+ class CedricMap(LabMap):
9
+ """
10
+ Class for handling a CNAM Cedric team from its name.
11
+ Default to `roc` team.
12
+ """
13
+ name = "roc"
14
+ base_url = "https://cedric.cnam.fr"
15
+
16
+ def _author_iterator(self):
17
+ soup = Soup(get(f"{self.base_url}/equipes/{self.name}/"), features="lxml")
18
+ searchers = [li.a for ul in soup.find('div', {'id': 'annuaire'})('ul')[:3] for li in ul('li')]
19
+ done = set()
20
+ for searcher in searchers:
21
+ name = searcher.text.split('(')[0].strip()
22
+ if name in done:
23
+ continue
24
+ url = f"{self.base_url}{searcher['href']}"
25
+ sousoup = Soup(get(url), features="lxml")
26
+ img = sousoup.find('img', {'class': 'photo'})['src']
27
+ response = requests.head(img, allow_redirects=True)
28
+ if int(response.headers.get("Content-Length")) < 3000:
29
+ img = None
30
+ done.add(name)
31
+ yield LabAuthor(name=name, metadata=AuthorMetadata(url=url, img=img, group=self.name.upper()))
32
+
33
+
34
+ class CedricFull(LabMap):
35
+ """
36
+ Class for handling all CNAM Cedric teams using `https://cedric.cnam.fr/equipes` to get team names.
37
+ """
38
+ name = "Cedric"
39
+
40
+ def _author_iterator(self):
41
+ base = "https://cedric.cnam.fr/equipes/"
42
+ soup = Soup(get(base), features="lxml")
43
+ teams = {a['href'].split('/')[-2] for a in soup('a') if base in a.get('href', "") and len(a['href'])>len(base)}
44
+ for team in teams:
45
+ for author in CedricMap(name=team)._author_iterator():
46
+ yield author
@@ -0,0 +1,43 @@
1
+ from bs4 import BeautifulSoup as Soup
2
+
3
+ from gismap.lab import LabAuthor
4
+ from gismap.lab.lab_author import AuthorMetadata
5
+ from gismap.lab.labmap import LabMap
6
+ from gismap.utils.requests import get
7
+
8
+
9
+ def lamsade_parse(div):
10
+ """
11
+ Parameters
12
+ ----------
13
+ div: :class:`~bs4.BeautifulSoup`
14
+ Soup of the div of one researcher
15
+
16
+ Returns
17
+ -------
18
+ :class:`tuple`
19
+ name, image url (or None), webpage (or None)
20
+ """
21
+ img = div.img['src'] if div.img else None
22
+ url = div.a['href'] if div.a else None
23
+ name = div.h2.text.strip().title()
24
+ name = " ".join(name.split(" ", 1)[::-1])
25
+ return name, img, url
26
+
27
+
28
+ class Lamsade(LabMap):
29
+ """
30
+ Class for handling the Lamsade team (Dauphine).
31
+ """
32
+
33
+ name = "Lamsade"
34
+ base_url = "https://www.lamsade.dauphine.fr/"
35
+ directory = "fr/personnes/enseignants-chercheurs-et-chercheurs.html"
36
+
37
+ def _author_iterator(self):
38
+ soup = Soup(get(self.base_url+self.directory), features="lxml")
39
+ for a in soup('div', class_="dauphinecv-item"):
40
+ name, img, url = lamsade_parse(a)
41
+ img = self.base_url+img if img else None
42
+ url = self.base_url+url if url else None
43
+ yield LabAuthor(name=name, metadata=AuthorMetadata(url=url, img=img, group=self.name))
@@ -22,7 +22,7 @@ class LINCS(Map):
22
22
  name = "LINCS"
23
23
 
24
24
  def _author_iterator(self):
25
- soup = Soup(get("https://www.lincs.fr/people/"))
25
+ soup = Soup(get("https://www.lincs.fr/people/"), features="lxml")
26
26
  for entry in soup.main("div", class_="trombinoscope-row"):
27
27
  cols = entry("div")
28
28
  name = cols[1].text
@@ -36,7 +36,7 @@ class LINCS(Map):
36
36
  if group:
37
37
  group = group[-1].text
38
38
  else:
39
- group = None
39
+ group = "External"
40
40
  author = LabAuthor(name)
41
41
  author.metadata.img = img
42
42
  author.metadata.group = group
@@ -29,7 +29,21 @@ class LaasMap(LabMap):
29
29
  if "public_avatar" in a.img["class"]
30
30
  else None
31
31
  )
32
- yield LabAuthor(name=name, metadata=AuthorMetadata(url=url, img=img))
32
+ yield LabAuthor(name=name, metadata=AuthorMetadata(url=url, img=img, group=self.name.upper()))
33
+
34
+
35
+ class LaasFull(LabMap):
36
+ """
37
+ Class for handling all LAAS teams using `https://www.laas.fr/fr/equipes/` to get team names.
38
+ """
39
+ name = "LAAS"
40
+
41
+ def _author_iterator(self):
42
+ soup = Soup(get("https://www.laas.fr/fr/equipes/"), features="lxml")
43
+ teams = [a['href'].split('/')[-2] for a in soup('a', {'class': "badge"})]
44
+ for team in teams:
45
+ for author in LaasMap(name=team)._author_iterator():
46
+ yield author
33
47
 
34
48
 
35
49
  class SolaceMap(LabMap):
@@ -41,8 +55,11 @@ class SolaceMap(LabMap):
41
55
  regex = re.compile(r"<li>(.*?)(,| \(|</li>)")
42
56
 
43
57
  def _author_iterator(self):
44
- html = get("https://solace.cnrs.fr/people.html")
58
+ html = get("https://solace.cnrs.fr/people.html", verify=False)
45
59
  for name, _ in self.regex.findall(html):
46
60
  soup = Soup(name, features="lxml")
47
61
  url = soup.a["href"] if soup.a else None
48
- yield LabAuthor(name=soup.text.strip(), metadata=AuthorMetadata(url=url))
62
+ yield LabAuthor(
63
+ name=soup.text.strip(),
64
+ metadata=AuthorMetadata(url=url, group=self.name.upper()),
65
+ )