gismap 0.2.2__py3-none-any.whl → 0.3.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.
- gismap/__init__.py +1 -0
- gismap/gisgraphs/__init__.py +0 -0
- gismap/gisgraphs/builder.py +105 -0
- gismap/{lab → gisgraphs}/graph.py +70 -66
- gismap/gisgraphs/groups.py +70 -0
- gismap/gisgraphs/js.py +190 -0
- gismap/gisgraphs/options.py +37 -0
- gismap/gisgraphs/style.py +119 -0
- gismap/gisgraphs/widget.py +145 -0
- gismap/lab/__init__.py +0 -4
- gismap/lab/egomap.py +6 -7
- gismap/lab/expansion.py +7 -6
- gismap/lab/filters.py +1 -1
- gismap/lab/lab_author.py +51 -4
- gismap/lab/labmap.py +4 -2
- gismap/lab_examples/__init__.py +0 -0
- gismap/lab_examples/cedric.py +46 -0
- gismap/{lab → lab_examples}/lincs.py +2 -2
- gismap/{lab → lab_examples}/toulouse.py +20 -3
- gismap/sources/dblp.py +15 -17
- gismap/sources/hal.py +17 -8
- gismap/sources/models.py +7 -0
- gismap/sources/multi.py +23 -15
- gismap/utils/requests.py +4 -2
- {gismap-0.2.2.dist-info → gismap-0.3.0.dist-info}/METADATA +21 -5
- gismap-0.3.0.dist-info/RECORD +38 -0
- gismap/lab/vis.py +0 -329
- gismap-0.2.2.dist-info/RECORD +0 -30
- /gismap/{lab → lab_examples}/lip6.py +0 -0
- {gismap-0.2.2.dist-info → gismap-0.3.0.dist-info}/WHEEL +0 -0
- {gismap-0.2.2.dist-info → gismap-0.3.0.dist-info}/licenses/AUTHORS.md +0 -0
|
@@ -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, dblp: 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", "DBLP", "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 "dblp"
|
|
104
|
+
if self.dbs.value == "DBLP"
|
|
105
|
+
else ["hal", "dblp"]
|
|
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'
|
|
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=
|
|
40
|
-
kwargs
|
|
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
|
|
77
|
-
count_coauthors[
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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 = [
|
|
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,7 +1,8 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
|
+
import re
|
|
2
3
|
|
|
3
4
|
from gismap import get_classes, HAL, DBLP
|
|
4
|
-
from gismap.sources.models import DB
|
|
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
|
|
@@ -26,6 +27,8 @@ class AuthorMetadata(LazyRepr):
|
|
|
26
27
|
Group of the author.
|
|
27
28
|
position: :class:`tuple`
|
|
28
29
|
Coordinates of the author.
|
|
30
|
+
keys: :class:`dict`
|
|
31
|
+
Some DB key values of the author.
|
|
29
32
|
"""
|
|
30
33
|
|
|
31
34
|
url: str = None
|
|
@@ -36,6 +39,26 @@ class AuthorMetadata(LazyRepr):
|
|
|
36
39
|
|
|
37
40
|
@dataclass(repr=False)
|
|
38
41
|
class LabAuthor(SourcedAuthor):
|
|
42
|
+
"""
|
|
43
|
+
Examples
|
|
44
|
+
---------
|
|
45
|
+
The metadata and DB key(s) of an author can be entered in parentheses using key/values.
|
|
46
|
+
|
|
47
|
+
Improper key/values are ignored (with a warning).
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
>>> dummy= LabAuthor("My Name(img: https://my.url.img, group:me,url:https://mysite.org,hal:key1,dblp:toto,badkey:hello,no_colon_separator)")
|
|
51
|
+
>>> dummy.metadata
|
|
52
|
+
AuthorMetadata(url='https://mysite.org', img='https://my.url.img', group='me')
|
|
53
|
+
>>> dummy.sources
|
|
54
|
+
[HALAuthor(name='My Name', key='key1'), DBLPAuthor(name='My Name', key='toto')]
|
|
55
|
+
|
|
56
|
+
You can enter multiple keys for the same DB. HAL key types are automatically detected.
|
|
57
|
+
|
|
58
|
+
>>> dummy2= LabAuthor("My Name (hal:key1,hal:123456,hal: My Other Name )")
|
|
59
|
+
>>> dummy2.sources
|
|
60
|
+
[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')]
|
|
61
|
+
"""
|
|
39
62
|
metadata: AuthorMetadata = field(default_factory=AuthorMetadata)
|
|
40
63
|
|
|
41
64
|
def auto_img(self):
|
|
@@ -45,6 +68,30 @@ class LabAuthor(SourcedAuthor):
|
|
|
45
68
|
self.metadata.img = img
|
|
46
69
|
break
|
|
47
70
|
|
|
71
|
+
def __post_init__(self):
|
|
72
|
+
pattern = r"\s*([^,(]+)\s*(?:\(([^)]*)\))?\s*$"
|
|
73
|
+
match = re.match(pattern, self.name)
|
|
74
|
+
if match:
|
|
75
|
+
self.name = match.group(1).strip()
|
|
76
|
+
content = match.group(2)
|
|
77
|
+
if content:
|
|
78
|
+
for kv in content.split(","):
|
|
79
|
+
if ":" not in kv:
|
|
80
|
+
logger.warning(f"I don't know what to do with {kv}.")
|
|
81
|
+
continue
|
|
82
|
+
k, v = kv.split(":", 1)
|
|
83
|
+
k = k.strip().lower()
|
|
84
|
+
v = v.strip()
|
|
85
|
+
if k in db_dict:
|
|
86
|
+
DBAuthor = db_class_to_auth_class(db_dict[k])
|
|
87
|
+
self.sources.append(DBAuthor(name=self.name, key=v))
|
|
88
|
+
elif k in ["url", "img", "group"]:
|
|
89
|
+
setattr(self.metadata, k, v)
|
|
90
|
+
else:
|
|
91
|
+
logger.warning(f"I don't know what to do with {kv}.")
|
|
92
|
+
else:
|
|
93
|
+
self.name = self.name.strip()
|
|
94
|
+
|
|
48
95
|
def auto_sources(self, dbs=None):
|
|
49
96
|
"""
|
|
50
97
|
Automatically populate the sources based on author's name.
|
|
@@ -63,9 +110,9 @@ class LabAuthor(SourcedAuthor):
|
|
|
63
110
|
for db in dbs:
|
|
64
111
|
source = db.search_author(self.name)
|
|
65
112
|
if len(source) == 0:
|
|
66
|
-
logger.
|
|
113
|
+
logger.info(f"{self.name} not found in {db.db_name}")
|
|
67
114
|
elif len(source) > 1:
|
|
68
|
-
logger.
|
|
115
|
+
logger.info(f"Multiple entries for {self.name} in {db.db_name}")
|
|
69
116
|
sources += source
|
|
70
117
|
if len(sources) > 0:
|
|
71
118
|
self.sources = sort_author_sources(sources)
|
|
@@ -74,7 +121,7 @@ class LabAuthor(SourcedAuthor):
|
|
|
74
121
|
def labify_author(author, rosetta):
|
|
75
122
|
if isinstance(author, LabAuthor):
|
|
76
123
|
return author
|
|
77
|
-
return rosetta.get(author.key, author)
|
|
124
|
+
return rosetta.get(author.key, rosetta.get(author.name, author))
|
|
78
125
|
|
|
79
126
|
|
|
80
127
|
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.
|
|
26
|
+
from gismap.gisgraphs.builder import make_vis
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class LabMap(MixInIO):
|
|
@@ -97,6 +97,8 @@ class LabMap(MixInIO):
|
|
|
97
97
|
self.authors[author.key] = author
|
|
98
98
|
if author.metadata.img is None:
|
|
99
99
|
author.auto_img()
|
|
100
|
+
if author.metadata.group is None:
|
|
101
|
+
author.metadata.group = self.name
|
|
100
102
|
|
|
101
103
|
def update_publis(self, desc="Publications information"):
|
|
102
104
|
"""
|
|
@@ -151,7 +153,7 @@ class LabMap(MixInIO):
|
|
|
151
153
|
return None
|
|
152
154
|
|
|
153
155
|
def html(self, **kwargs):
|
|
154
|
-
return
|
|
156
|
+
return make_vis(self, **kwargs)
|
|
155
157
|
|
|
156
158
|
def save_html(self, name=None, **kwargs):
|
|
157
159
|
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
|
|
@@ -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 =
|
|
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(
|
|
62
|
+
yield LabAuthor(
|
|
63
|
+
name=soup.text.strip(),
|
|
64
|
+
metadata=AuthorMetadata(url=url, group=self.name.upper()),
|
|
65
|
+
)
|
gismap/sources/dblp.py
CHANGED
|
@@ -72,23 +72,6 @@ class DBLP(DB):
|
|
|
72
72
|
Papers available in DBLP.
|
|
73
73
|
wait: :class:`bool`
|
|
74
74
|
Wait a bit to avoid 429.
|
|
75
|
-
|
|
76
|
-
Examples
|
|
77
|
-
--------
|
|
78
|
-
|
|
79
|
-
>>> fabien = DBLPAuthor('Fabien Mathieu', key='66/2077')
|
|
80
|
-
>>> publications = sorted(DBLP.from_author(fabien),
|
|
81
|
-
... key=lambda p: p.title)
|
|
82
|
-
>>> publications[0] # doctest: +NORMALIZE_WHITESPACE
|
|
83
|
-
DBLPPublication(title='Achievable catalog size in peer-to-peer video-on-demand systems.',
|
|
84
|
-
authors=[DBLPAuthor(name='Yacine Boufkhad', key='75/5742'), DBLPAuthor(name='Fabien Mathieu', key='66/2077'),
|
|
85
|
-
DBLPAuthor(name='Fabien de Montgolfier', key='57/6313'), DBLPAuthor(name='Diego Perino', key='03/3645'),
|
|
86
|
-
DBLPAuthor(name='Laurent Viennot', key='v/LaurentViennot')],
|
|
87
|
-
venue='IPTPS', type='conference', year=2008, key='conf/iptps/BoufkhadMMPV08')
|
|
88
|
-
>>> publications[-1] # doctest: +NORMALIZE_WHITESPACE
|
|
89
|
-
DBLPPublication(title='Upper Bounds for Stabilization in Acyclic Preference-Based Systems.',
|
|
90
|
-
authors=[DBLPAuthor(name='Fabien Mathieu', key='66/2077')], venue='SSS', type='conference', year=2007,
|
|
91
|
-
key='conf/sss/Mathieu07')
|
|
92
75
|
"""
|
|
93
76
|
r = get(f"https://dblp.org/pid/{a.key}.xml")
|
|
94
77
|
soup = Soup(r, features="xml")
|
|
@@ -100,6 +83,21 @@ class DBLP(DB):
|
|
|
100
83
|
|
|
101
84
|
@dataclass(repr=False)
|
|
102
85
|
class DBLPAuthor(Author, DBLP):
|
|
86
|
+
"""
|
|
87
|
+
Examples
|
|
88
|
+
--------
|
|
89
|
+
|
|
90
|
+
>>> fabien = DBLPAuthor('Fabien Mathieu', key='66/2077')
|
|
91
|
+
>>> publications = sorted(fabien.get_publications(),
|
|
92
|
+
... key=lambda p: p.title)
|
|
93
|
+
>>> publications[0].url # doctest: +NORMALIZE_WHITESPACE
|
|
94
|
+
'https://dblp.org/rec/conf/iptps/BoufkhadMMPV08.html'
|
|
95
|
+
>>> publications[-1] # doctest: +NORMALIZE_WHITESPACE
|
|
96
|
+
DBLPPublication(title='Upper Bounds for Stabilization in Acyclic Preference-Based Systems.',
|
|
97
|
+
authors=[DBLPAuthor(name='Fabien Mathieu', key='66/2077')], venue='SSS', type='conference', year=2007,
|
|
98
|
+
key='conf/sss/Mathieu07')
|
|
99
|
+
|
|
100
|
+
"""
|
|
103
101
|
key: str
|
|
104
102
|
aliases: list = field(default_factory=list)
|
|
105
103
|
|
gismap/sources/hal.py
CHANGED
|
@@ -44,8 +44,7 @@ class HAL(DB):
|
|
|
44
44
|
>>> HAL.search_author("Ana Busic")
|
|
45
45
|
[HALAuthor(name='Ana Busic', key='anabusic')]
|
|
46
46
|
>>> HAL.search_author("Potop-Butucaru Maria") # doctest: +NORMALIZE_WHITESPACE
|
|
47
|
-
[HALAuthor(name='Potop-Butucaru Maria', key='
|
|
48
|
-
HALAuthor(name='Potop-Butucaru Maria', key='841868', key_type='pid')]
|
|
47
|
+
[HALAuthor(name='Potop-Butucaru Maria', key='841868', key_type='pid')]
|
|
49
48
|
>>> diego = HAL.search_author("Diego Perino")
|
|
50
49
|
>>> diego # doctest: +NORMALIZE_WHITESPACE
|
|
51
50
|
[HALAuthor(name='Diego Perino', key='847558', key_type='pid'),
|
|
@@ -134,12 +133,12 @@ class HAL(DB):
|
|
|
134
133
|
|
|
135
134
|
>>> maria = HAL.search_author('Maria Potop-Butucaru')
|
|
136
135
|
>>> maria # doctest: +NORMALIZE_WHITESPACE
|
|
137
|
-
[HALAuthor(name='Maria Potop-Butucaru', key='
|
|
138
|
-
|
|
139
|
-
>>>
|
|
140
|
-
|
|
141
|
-
>>> len(maria[
|
|
142
|
-
|
|
136
|
+
[HALAuthor(name='Maria Potop-Butucaru', key='841868', key_type='pid')]
|
|
137
|
+
>>> n_pubs = len(HAL.from_author(maria[0]))
|
|
138
|
+
>>> n_pubs > 200
|
|
139
|
+
True
|
|
140
|
+
>>> n_pubs == len(maria[0].get_publications())
|
|
141
|
+
True
|
|
143
142
|
|
|
144
143
|
Note: an error is raised if not enough data is provided
|
|
145
144
|
|
|
@@ -173,6 +172,9 @@ class HAL(DB):
|
|
|
173
172
|
r = get(api, params=params)
|
|
174
173
|
response = json.loads(r)["response"]
|
|
175
174
|
res = [HALPublication.from_json(r) for r in response.get("docs", [])]
|
|
175
|
+
if len(res) == 0 and a.key_type != "fullname":
|
|
176
|
+
name = a.name
|
|
177
|
+
return HAL.from_author(HALAuthor(name=name, key=name, key_type="fullname"))
|
|
176
178
|
return res
|
|
177
179
|
|
|
178
180
|
|
|
@@ -185,6 +187,13 @@ class HALAuthor(Author, HAL):
|
|
|
185
187
|
_img: str = None
|
|
186
188
|
_cv: bool = None
|
|
187
189
|
|
|
190
|
+
def __post_init__(self):
|
|
191
|
+
if self.key and self.key_type is None:
|
|
192
|
+
if self.key.isdigit():
|
|
193
|
+
self.key_type = "pid"
|
|
194
|
+
if " " in self.key:
|
|
195
|
+
self.key_type = "fullname"
|
|
196
|
+
|
|
188
197
|
def check_cv(self):
|
|
189
198
|
if self.key_type is not None:
|
|
190
199
|
self._cv = False
|