initial commit
This commit is contained in:
commit
5e6967def2
13 changed files with 586744 additions and 0 deletions
2
README.md
Normal file
2
README.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Linkedin posts scrapper
|
||||||
|
|
15
construct_hugo_content_links.py
Normal file
15
construct_hugo_content_links.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# scan the true content folder and recreate all the symbolic links
|
||||||
|
|
||||||
|
from os.path import basename
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
SITE_BASE = "./janco_website"
|
||||||
|
MD_ORGANIZED_CONTENT_BASE = "./janco_website/true_content/posts"
|
||||||
|
MD_FLAT_CONTENT_BASE = "./janco_website/content/posts"
|
||||||
|
|
||||||
|
for path in Path(MD_ORGANIZED_CONTENT_BASE).rglob('*.md'):
|
||||||
|
bn = basename(path)
|
||||||
|
Path(MD_FLAT_CONTENT_BASE + '/' + bn).symlink_to('../../' + str(path.relative_to(SITE_BASE)))
|
||||||
|
|
||||||
|
|
112
gen_content.py
Normal file
112
gen_content.py
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
|
||||||
|
#se generate markdown that can be used with hugo
|
||||||
|
|
||||||
|
# A single JSON file that contains all the posts that we want to generate
|
||||||
|
# This file contains a JSON array with linkedin user profile posts
|
||||||
|
INP_FILE_PATH = "./out_posts_all_1.json"
|
||||||
|
# This file hold the current state of scrapping, what articles have been scrapped
|
||||||
|
STORAGE_FILE_PATH = "./posts_scrapping_state.json"
|
||||||
|
|
||||||
|
MD_ORGANIZED_CONTENT_BASE = "./janco_website/true_content/posts"
|
||||||
|
MD_FLAT_CONTENT_BASE = "./janco_website/content/posts"
|
||||||
|
|
||||||
|
import json
|
||||||
|
from urllib.parse import urljoin, urlparse
|
||||||
|
import uuid
|
||||||
|
import os
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
def slurp_file_and_parse(inp_file_path):
|
||||||
|
posts = []
|
||||||
|
with open(inp_file_path) as inp_file:
|
||||||
|
posts = json.loads(inp_file.read())
|
||||||
|
return posts
|
||||||
|
|
||||||
|
posts = slurp_file_and_parse(INP_FILE_PATH)
|
||||||
|
|
||||||
|
from pprint import pprint
|
||||||
|
from slugify import slugify
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Post:
|
||||||
|
id: int
|
||||||
|
title: str
|
||||||
|
created_at: datetime
|
||||||
|
content: str
|
||||||
|
original_url: str
|
||||||
|
|
||||||
|
@property
|
||||||
|
def slug(self) -> str:
|
||||||
|
return slugify(self.title)
|
||||||
|
|
||||||
|
|
||||||
|
ARTICLE_COMPONENT = "com.linkedin.voyager.feed.render.ArticleComponent"
|
||||||
|
|
||||||
|
global_count = 0
|
||||||
|
|
||||||
|
def parse_post(raw_post):
|
||||||
|
global global_count
|
||||||
|
|
||||||
|
def datetime_from_id(id: int):
|
||||||
|
# thanks to https://github.com/Ollie-Boyd/Linkedin-post-timestamp-extractor
|
||||||
|
epoch = (id & 0xffffffffff800000) >> 22
|
||||||
|
return datetime.utcfromtimestamp(epoch/1000)
|
||||||
|
|
||||||
|
entity_id = int(raw_post['entityUrn'].split("activity:")[1].split(',')[0])
|
||||||
|
|
||||||
|
original_url = next(filter(lambda x: x['actionType'] == "SHARE_VIA", raw_post['updateMetadata']['updateActions']['actions']))['url']
|
||||||
|
original_url = urljoin(original_url, urlparse(original_url).path)
|
||||||
|
|
||||||
|
created_at = datetime_from_id(entity_id)
|
||||||
|
|
||||||
|
global_count += 1
|
||||||
|
out_post = Post(
|
||||||
|
id=entity_id,
|
||||||
|
title=f"CHANGE_ME {global_count}",
|
||||||
|
content=raw_post['commentary']['text']['text'],
|
||||||
|
created_at=created_at.replace(microsecond=0),
|
||||||
|
original_url=original_url
|
||||||
|
# title=raw_post
|
||||||
|
)
|
||||||
|
|
||||||
|
# content_items = list(raw_post['content'].items())
|
||||||
|
components = list(raw_post['content'].keys()) if 'content' in raw_post else []
|
||||||
|
# the "content" key contains some other components like the com.linkedin.voyager.feed.render.ArticleComponent that indicate there is an article (linked) attached to this post
|
||||||
|
|
||||||
|
if ARTICLE_COMPONENT in components:
|
||||||
|
article = raw_post['content'][ARTICLE_COMPONENT]
|
||||||
|
# pprint(article, depth=1)
|
||||||
|
# image_path = article['largeImage']['attributes'][0]['vectorImage']['artifacts'][0]['fileIdentifyingUrlPathSegment']
|
||||||
|
|
||||||
|
return out_post
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
for post in posts:
|
||||||
|
pp = parse_post(post)
|
||||||
|
|
||||||
|
|
||||||
|
pp.created_at.year
|
||||||
|
pp.created_at.month
|
||||||
|
|
||||||
|
yaml_header = yaml.dump({
|
||||||
|
'title': pp.title,
|
||||||
|
'date': pp.created_at.isoformat(),
|
||||||
|
'li-id': pp.id,
|
||||||
|
'li-url': pp.original_url,
|
||||||
|
})
|
||||||
|
dir_container = f"{MD_ORGANIZED_CONTENT_BASE}/{pp.created_at.year}/{pp.created_at.month:02}"
|
||||||
|
if not os.path.isdir(dir_container):
|
||||||
|
os.makedirs(dir_container)
|
||||||
|
|
||||||
|
with open(f"{dir_container}/{pp.slug}.md", 'w') as f:
|
||||||
|
f.write("\n".join([
|
||||||
|
"---",
|
||||||
|
yaml_header.strip(),
|
||||||
|
"---",
|
||||||
|
"",
|
||||||
|
pp.content
|
||||||
|
]))
|
||||||
|
|
1
janco_posts.json
Normal file
1
janco_posts.json
Normal file
File diff suppressed because one or more lines are too long
48108
out_posts.json
Normal file
48108
out_posts.json
Normal file
File diff suppressed because it is too large
Load diff
48108
out_posts_1.json
Normal file
48108
out_posts_1.json
Normal file
File diff suppressed because it is too large
Load diff
48108
out_posts_2.json
Normal file
48108
out_posts_2.json
Normal file
File diff suppressed because it is too large
Load diff
441665
out_posts_all_1.json
Normal file
441665
out_posts_all_1.json
Normal file
File diff suppressed because it is too large
Load diff
33
propagate_title_change_to_slug.py
Normal file
33
propagate_title_change_to_slug.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# this script will look for each markdown with YAML frontmatter header and will rename the file name to the slug from the title in the header
|
||||||
|
|
||||||
|
from slugify import slugify
|
||||||
|
|
||||||
|
# https://pypi.org/project/python-frontmatter/
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pyaml
|
||||||
|
import json
|
||||||
|
import frontmatter
|
||||||
|
|
||||||
|
INP_DIR = "./janco_website/true_content/posts"
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
renamed = 0
|
||||||
|
for path in Path(INP_DIR).rglob('*.md'):
|
||||||
|
print(path)
|
||||||
|
post = frontmatter.load(path)
|
||||||
|
if post['title'].startswith("CHANGE_ME"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_name = slugify.slugify(post['title']) + '.md'
|
||||||
|
if new_name == path:
|
||||||
|
print("same")
|
||||||
|
continue
|
||||||
|
|
||||||
|
print("rename")
|
||||||
|
os.rename(path, path.parent / ('/' + new_name))
|
||||||
|
renamed += 1
|
||||||
|
# path, slugify.slugify(post['title'])
|
||||||
|
|
||||||
|
print(f"{renamed} files renamed")
|
65
scrape.py
Normal file
65
scrape.py
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
from linkedin_api import Linkedin
|
||||||
|
import os
|
||||||
|
|
||||||
|
def fetch_posts(client, public_id=None, urn_id=None, post_count=10):
|
||||||
|
"""
|
||||||
|
get_profile_posts: Get profile posts
|
||||||
|
|
||||||
|
:param public_id: LinkedIn public ID for a profile
|
||||||
|
:type public_id: str, optional
|
||||||
|
:param urn_id: LinkedIn URN ID for a profile
|
||||||
|
:type urn_id: str, optional
|
||||||
|
:param post_count: Number of posts to fetch
|
||||||
|
:type post_count: int, optional
|
||||||
|
:return: List of posts
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
url_params = {
|
||||||
|
"count": min(post_count, client._MAX_POST_COUNT),
|
||||||
|
"start": 0,
|
||||||
|
"q": "memberShareFeed",
|
||||||
|
"moduleKey": "member-shares:phone",
|
||||||
|
"includeLongTermHistory": True,
|
||||||
|
}
|
||||||
|
if urn_id:
|
||||||
|
profile_urn = f"urn:li:fsd_profile:{urn_id}"
|
||||||
|
else:
|
||||||
|
profile = client.get_profile(public_id=public_id)
|
||||||
|
profile_urn = profile["profile_urn"].replace(
|
||||||
|
"fs_miniProfile", "fsd_profile"
|
||||||
|
)
|
||||||
|
url_params["profileUrn"] = profile_urn
|
||||||
|
url = f"/identity/profileUpdatesV2"
|
||||||
|
res = client._fetch(url, params=url_params)
|
||||||
|
data = res.json()
|
||||||
|
if data and "status" in data and data["status"] != 200:
|
||||||
|
client.logger.info("request failed: {}".format(data["message"]))
|
||||||
|
return {}
|
||||||
|
while data and data["metadata"]["paginationToken"] != "":
|
||||||
|
print(f"got {len(data['elements'])=}")
|
||||||
|
if len(data["elements"]) >= post_count:
|
||||||
|
break
|
||||||
|
pagination_token = data["metadata"]["paginationToken"]
|
||||||
|
url_params["start"] = url_params["start"] + client._MAX_POST_COUNT
|
||||||
|
url_params["paginationToken"] = pagination_token
|
||||||
|
print("new request", url_params)
|
||||||
|
res = client._fetch(url, params=url_params)
|
||||||
|
data["metadata"] = res.json()["metadata"]
|
||||||
|
data["elements"] = data["elements"] + res.json()["elements"]
|
||||||
|
data["paging"] = res.json()["paging"]
|
||||||
|
return data["elements"]
|
||||||
|
|
||||||
|
# Authenticate using any Linkedin account credentials
|
||||||
|
api = Linkedin(os.getenv("LINKEDIN_USERNAME"), os.getenv("LINKEDIN_PASSWORD"))
|
||||||
|
|
||||||
|
# GET a profile
|
||||||
|
# profile = api.get_profile('jean-marc-jancovici')
|
||||||
|
# profile_id = "ACoAAAYJ-P4B8XcfHOiVTdmiAyYGfvxBRs3J_Ug"
|
||||||
|
urn_id = "ACoAAAYJ-P4B8XcfHOiVTdmiAyYGfvxBRs3J_Ug"
|
||||||
|
|
||||||
|
posts = fetch_posts(api, urn_id=urn_id, post_count=800)
|
||||||
|
|
||||||
|
import json
|
||||||
|
with open('./out_posts_all_1.json', 'w') as f:
|
||||||
|
f.write(json.dumps(posts, indent=4))
|
||||||
|
|
65
title_generator/generate.py
Normal file
65
title_generator/generate.py
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import json
|
||||||
|
import yaml
|
||||||
|
import websocket
|
||||||
|
import os
|
||||||
|
|
||||||
|
BASE_DIR = '../janco_website/content/posts/'
|
||||||
|
OUTPUT_FILE = './output_shit.json'
|
||||||
|
|
||||||
|
files = os.scandir(BASE_DIR)
|
||||||
|
|
||||||
|
with open(OUTPUT_FILE, 'w') as of:
|
||||||
|
of.write("[\n")
|
||||||
|
|
||||||
|
def write_result(data):
|
||||||
|
with open(OUTPUT_FILE, 'a') as of:
|
||||||
|
of.write(json.dumps(data))
|
||||||
|
|
||||||
|
def send_msg(sess, data):
|
||||||
|
print("Send_msg", data)
|
||||||
|
sess.send(json.dumps(data))
|
||||||
|
|
||||||
|
def talk(sess, inputs):
|
||||||
|
send_msg(sess, {"type":"generate","inputs":inputs,"max_new_tokens":1,"stop_sequence":"</s>","extra_stop_sequences":["\n\nHuman"],"do_sample":1,"temperature":0.9,"top_k":40})
|
||||||
|
|
||||||
|
def collect_response(sess):
|
||||||
|
all_text = ""
|
||||||
|
while True:
|
||||||
|
raw = sess.recv()
|
||||||
|
print(raw)
|
||||||
|
res = json.loads(raw)
|
||||||
|
if 'stop' in res and res['stop']:
|
||||||
|
return all_text
|
||||||
|
all_text += res['outputs']
|
||||||
|
|
||||||
|
# https://websocket-client.readthedocs.io/en/latest/examples.html
|
||||||
|
sess = websocket.WebSocket()
|
||||||
|
sess.connect("ws://chat.petals.ml/api/v2/generate")
|
||||||
|
|
||||||
|
# Open model
|
||||||
|
send_msg(sess, {"type":"open_inference_session","model":"bigscience/bloomz-petals","max_length":1024})
|
||||||
|
print(sess.recv())
|
||||||
|
|
||||||
|
send_msg(sess, {"type":"generate","inputs":"A human talks to a powerful AI that follows the human's instructions.\n\nHuman: Hi!\n\nAI: Hi! How can I help you?</s>Human: Hello\n\nAI:","max_new_tokens":1,"stop_sequence":"</s>","extra_stop_sequences":["\n\nHuman"],"do_sample":1,"temperature":0.9,"top_k":40})
|
||||||
|
collect_response(sess)
|
||||||
|
|
||||||
|
|
||||||
|
for fname in files:
|
||||||
|
print("=========")
|
||||||
|
print(f"opening {fname.path}")
|
||||||
|
with open(fname.path) as inp_f:
|
||||||
|
file_content = inp_f.read()
|
||||||
|
compos = file_content.split('---')
|
||||||
|
headers = yaml.safe_load(compos[1])
|
||||||
|
content = compos[2].strip()
|
||||||
|
|
||||||
|
print(headers)
|
||||||
|
print(content)
|
||||||
|
prefix = "Donnez un titre descriptif résumant ce texte :"
|
||||||
|
send_msg(sess, {"type":"generate","inputs": "Human: " + prefix + content + "\nAI:","max_new_tokens":1,"stop_sequence":"</s>","extra_stop_sequences":["\n\nHuman"],"do_sample":1,"temperature":0.9,"top_k":40})
|
||||||
|
generated_title = collect_response(sess)
|
||||||
|
print(generated_title)
|
||||||
|
write_result({"on_post_file_path": fname.path, "generated_title": generated_title, "on_post": headers})
|
||||||
|
|
||||||
|
with open(OUTPUT_FILE, 'a') as of:
|
||||||
|
of.write("\n]\n")
|
2
title_generator/output_shit.json
Normal file
2
title_generator/output_shit.json
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[
|
||||||
|
{"on_post_file_path": "../janco_website/content/posts/2021-05-10-08-17.md", "generated_title": " Plan de transformation de l'\u00e9conomie fran\u00e7aise", "on_post": {"date": "2021-05-10T08:17:53.344000", "li-id": 6797434492945481728, "li-url": "https://www.linkedin.com/posts/jean-marc-jancovici_comment-contribuer-au-plan-de-transformation-activity-6797434492945481728-YglP?utm_source=share&utm_medium=member_desktop", "title": "2021-05-10 08:17"}}{"on_post_file_path": "../janco_website/content/posts/2021-09-12-09-04.md", "generated_title": " Les prix de l'\u00e9lectricit\u00e9 montent en fl\u00e8che. Qu'est-ce qui se passe?", "on_post": {"date": "2021-09-12T09:04:37.616000", "li-id": 6842744738118959104, "li-url": "https://www.linkedin.com/posts/jean-marc-jancovici_les-prix-de-l%C3%A9lectricit%C3%A9-ont-fortement-augment%C3%A9-activity-6842744738118959104-VnLd?utm_source=share&utm_medium=member_desktop", "title": "2021-09-12 09:04"}}
|
460
tmp.json
Normal file
460
tmp.json
Normal file
|
@ -0,0 +1,460 @@
|
||||||
|
{
|
||||||
|
"actor": {
|
||||||
|
"urn": "urn:li:member:101316862",
|
||||||
|
"image": {
|
||||||
|
"accessibilityTextAttributes": [],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"sourceType": "PROFILE_PICTURE",
|
||||||
|
"miniProfile": {
|
||||||
|
"firstName": "Jean-Marc",
|
||||||
|
"lastName": "Jancovici",
|
||||||
|
"dashEntityUrn": "urn:li:fsd_profile:ACoAAAYJ-P4B8XcfHOiVTdmiAyYGfvxBRs3J_Ug",
|
||||||
|
"occupation": "Founding Partner, Carbone 4 - President, The Shift project",
|
||||||
|
"objectUrn": "urn:li:member:101316862",
|
||||||
|
"entityUrn": "urn:li:fs_miniProfile:ACoAAAYJ-P4B8XcfHOiVTdmiAyYGfvxBRs3J_Ug",
|
||||||
|
"backgroundImage": {
|
||||||
|
"com.linkedin.common.VectorImage": {
|
||||||
|
"artifacts": [
|
||||||
|
{
|
||||||
|
"width": 800,
|
||||||
|
"fileIdentifyingUrlPathSegment": "200_800/0/1623047088606?e=1681948800&v=beta&t=LoNfzUx_oAOOk72H8rcovxufm3ojeyZN-2kh5jNXD1g",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 1400,
|
||||||
|
"fileIdentifyingUrlPathSegment": "350_1400/0/1623047088606?e=1681948800&v=beta&t=mIMZE7AxPY31jjVxA6aagUpWg8sYHVdTtMKz28Y9tZU",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 350
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rootUrl": "https://media.licdn.com/dms/image/C4D16AQFQkQUwXFS8Pg/profile-displaybackgroundimage-shrink_"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"publicIdentifier": "jean-marc-jancovici",
|
||||||
|
"picture": {
|
||||||
|
"com.linkedin.common.VectorImage": {
|
||||||
|
"artifacts": [
|
||||||
|
{
|
||||||
|
"width": 100,
|
||||||
|
"fileIdentifyingUrlPathSegment": "100_100/0/1621956071842?e=1681948800&v=beta&t=7Rlra5TVZVrH_dL8mO5xBbdGE3WGmkNbLwR5ttc0Og8",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 200,
|
||||||
|
"fileIdentifyingUrlPathSegment": "200_200/0/1621956071842?e=1681948800&v=beta&t=TKBiEX7E-NaEd00j4smYvfbncjKD8alhyOi5bAiVEA0",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 400,
|
||||||
|
"fileIdentifyingUrlPathSegment": "400_400/0/1621956071842?e=1681948800&v=beta&t=cZI5Kp_eoX797cjLZKJtWf9zCY_WfSimhbmq6cejUbo",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 800,
|
||||||
|
"fileIdentifyingUrlPathSegment": "800_800/0/1621956071842?e=1681948800&v=beta&t=5eFGnjR0Od4YDgflWEK-6u7-Vrie2dtGuiWlfGCJ3Hs",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 800
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rootUrl": "https://media.licdn.com/dms/image/C4D03AQEUVgCmKN3cqQ/profile-displayphoto-shrink_"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"trackingId": "zfxFwzQcTn6jC0cw3+VeRw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"supplementaryActorInfo": {
|
||||||
|
"textDirection": "USER_LOCALE",
|
||||||
|
"text": " • Following"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"textDirection": "FIRST_STRONG",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"start": 0,
|
||||||
|
"length": 19,
|
||||||
|
"miniProfile": {
|
||||||
|
"firstName": "Jean-Marc",
|
||||||
|
"lastName": "Jancovici",
|
||||||
|
"dashEntityUrn": "urn:li:fsd_profile:ACoAAAYJ-P4B8XcfHOiVTdmiAyYGfvxBRs3J_Ug",
|
||||||
|
"occupation": "Founding Partner, Carbone 4 - President, The Shift project",
|
||||||
|
"objectUrn": "urn:li:member:101316862",
|
||||||
|
"entityUrn": "urn:li:fs_miniProfile:ACoAAAYJ-P4B8XcfHOiVTdmiAyYGfvxBRs3J_Ug",
|
||||||
|
"backgroundImage": {
|
||||||
|
"com.linkedin.common.VectorImage": {
|
||||||
|
"artifacts": [
|
||||||
|
{
|
||||||
|
"width": 800,
|
||||||
|
"fileIdentifyingUrlPathSegment": "200_800/0/1623047088606?e=1681948800&v=beta&t=LoNfzUx_oAOOk72H8rcovxufm3ojeyZN-2kh5jNXD1g",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 1400,
|
||||||
|
"fileIdentifyingUrlPathSegment": "350_1400/0/1623047088606?e=1681948800&v=beta&t=mIMZE7AxPY31jjVxA6aagUpWg8sYHVdTtMKz28Y9tZU",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 350
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rootUrl": "https://media.licdn.com/dms/image/C4D16AQFQkQUwXFS8Pg/profile-displaybackgroundimage-shrink_"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"publicIdentifier": "jean-marc-jancovici",
|
||||||
|
"picture": {
|
||||||
|
"com.linkedin.common.VectorImage": {
|
||||||
|
"artifacts": [
|
||||||
|
{
|
||||||
|
"width": 100,
|
||||||
|
"fileIdentifyingUrlPathSegment": "100_100/0/1621956071842?e=1681948800&v=beta&t=7Rlra5TVZVrH_dL8mO5xBbdGE3WGmkNbLwR5ttc0Og8",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 200,
|
||||||
|
"fileIdentifyingUrlPathSegment": "200_200/0/1621956071842?e=1681948800&v=beta&t=TKBiEX7E-NaEd00j4smYvfbncjKD8alhyOi5bAiVEA0",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 400,
|
||||||
|
"fileIdentifyingUrlPathSegment": "400_400/0/1621956071842?e=1681948800&v=beta&t=cZI5Kp_eoX797cjLZKJtWf9zCY_WfSimhbmq6cejUbo",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 800,
|
||||||
|
"fileIdentifyingUrlPathSegment": "800_800/0/1621956071842?e=1681948800&v=beta&t=5eFGnjR0Od4YDgflWEK-6u7-Vrie2dtGuiWlfGCJ3Hs",
|
||||||
|
"expiresAt": 1681948800000,
|
||||||
|
"height": 800
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rootUrl": "https://media.licdn.com/dms/image/C4D03AQEUVgCmKN3cqQ/profile-displayphoto-shrink_"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"trackingId": "zfxFwzQcTn6jC0cw3+VeRw=="
|
||||||
|
},
|
||||||
|
"type": "PROFILE_FULLNAME"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"text": "Jean-Marc Jancovici"
|
||||||
|
},
|
||||||
|
"subDescription": {
|
||||||
|
"textDirection": "USER_LOCALE",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"start": 6,
|
||||||
|
"length": 1,
|
||||||
|
"artDecoIcon": "IC_GLOBE_16DP",
|
||||||
|
"type": "ART_DECO_ICON"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"text": "11h • ",
|
||||||
|
"accessibilityText": "11 hours ago"
|
||||||
|
},
|
||||||
|
"navigationContext": {
|
||||||
|
"trackingActionType": "viewMember",
|
||||||
|
"accessibilityText": "View Jean-Marc Jancovici’s profile",
|
||||||
|
"actionTarget": "https://www.linkedin.com/in/jean-marc-jancovici?miniProfileUrn=urn%3Ali%3Afs_miniProfile%3AACoAAAYJ-P4B8XcfHOiVTdmiAyYGfvxBRs3J_Ug"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"textDirection": "USER_LOCALE",
|
||||||
|
"text": "Founding Partner, Carbone 4 - President, The Shift project"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dashEntityUrn": "urn:li:fsd_update:(urn:li:activity:7032115801016938496,MEMBER_SHARES,EMPTY,DEFAULT,false)",
|
||||||
|
"updateMetadata": {
|
||||||
|
"urn": "urn:li:activity:7032115801016938496",
|
||||||
|
"actionsPosition": "ACTOR_COMPONENT",
|
||||||
|
"updateActions": {
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"accessibilityTextAttributes": [],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"sourceType": "SYSTEM_IMAGE",
|
||||||
|
"systemImage": "SYS_ICN_BOOKMARK_OUTLINE_MEDIUM"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"actionType": "SAVE",
|
||||||
|
"saveAction": {
|
||||||
|
"entityUrn": "urn:li:fs_saveAction:(SAVE,urn:li:activity:7032115801016938496)",
|
||||||
|
"saved": false,
|
||||||
|
"dashEntityUrn": "urn:li:fsd_saveState:(SAVE,urn:li:activity:7032115801016938496)"
|
||||||
|
},
|
||||||
|
"text": "Save"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"accessibilityTextAttributes": [],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"sourceType": "SYSTEM_IMAGE",
|
||||||
|
"systemImage": "SYS_ICN_LINK_MEDIUM"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"actionType": "SHARE_VIA",
|
||||||
|
"text": "Copy link to post",
|
||||||
|
"url": "https://www.linkedin.com/posts/jean-marc-jancovici_le-shift-recrute-une-cheffe-de-projet-agriculture-activity-7032115801016938496-lQSy?utm_source=share&utm_medium=member_desktop"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"accessibilityTextAttributes": [],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"sourceType": "SYSTEM_IMAGE",
|
||||||
|
"systemImage": "SYS_ICN_CLEAR_MEDIUM"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"actionType": "UNFOLLOW_MEMBER",
|
||||||
|
"text": "Unfollow Jean-Marc Jancovici",
|
||||||
|
"followAction": {
|
||||||
|
"followTrackingActionType": "followMember",
|
||||||
|
"followingInfo": {
|
||||||
|
"followingType": "FOLLOWING",
|
||||||
|
"entityUrn": "urn:li:fs_followingInfo:urn:li:member:ACoAAAYJ-P4B8XcfHOiVTdmiAyYGfvxBRs3J_Ug",
|
||||||
|
"dashFollowingStateUrn": "urn:li:fsd_followingState:urn:li:fsd_profile:ACoAAAYJ-P4B8XcfHOiVTdmiAyYGfvxBRs3J_Ug",
|
||||||
|
"following": true,
|
||||||
|
"trackingUrn": "urn:li:member:101316862"
|
||||||
|
},
|
||||||
|
"unfollowTrackingActionType": "unfollowMember",
|
||||||
|
"type": "UNFOLLOW_TOGGLE",
|
||||||
|
"trackingActionType": "unfollowMember"
|
||||||
|
},
|
||||||
|
"confirmationAction": {
|
||||||
|
"title": {
|
||||||
|
"text": "Post removed"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"textDirection": "USER_LOCALE",
|
||||||
|
"attributes": [],
|
||||||
|
"text": "You'll no longer see posts from Jean-Marc Jancovici in your feed"
|
||||||
|
},
|
||||||
|
"undoable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actionType": "REPORT",
|
||||||
|
"targetUrn": "urn:li:share:7032115800496848896",
|
||||||
|
"contentSource": "UGC_POST",
|
||||||
|
"authorProfileId": "ACoAAAYJ-P4B8XcfHOiVTdmiAyYGfvxBRs3J_Ug",
|
||||||
|
"authorUrn": "urn:li:member:101316862",
|
||||||
|
"icon": {
|
||||||
|
"accessibilityTextAttributes": [],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"sourceType": "SYSTEM_IMAGE",
|
||||||
|
"systemImage": "SYS_ICN_REPORT_MEDIUM"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"secondaryAction": {
|
||||||
|
"icon": {
|
||||||
|
"accessibilityTextAttributes": [],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"sourceType": "SYSTEM_IMAGE",
|
||||||
|
"systemImage": "SYS_ICN_VISIBILITY_OFF_MEDIUM"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"actionType": "HIDE_UPDATE",
|
||||||
|
"targetUrn": "urn:li:activity:7032115801016938496",
|
||||||
|
"text": "Hide this post",
|
||||||
|
"confirmationAction": {
|
||||||
|
"title": {
|
||||||
|
"textDirection": "USER_LOCALE",
|
||||||
|
"text": "Post removed"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"textDirection": "USER_LOCALE",
|
||||||
|
"text": "You'll no longer see this post in your feed."
|
||||||
|
},
|
||||||
|
"undoable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text": "Report post",
|
||||||
|
"confirmationAction": {
|
||||||
|
"title": {
|
||||||
|
"textDirection": "USER_LOCALE",
|
||||||
|
"text": "Thank you for your report"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"textDirection": "USER_LOCALE",
|
||||||
|
"text": "We appreciate you letting us know"
|
||||||
|
},
|
||||||
|
"undoable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entityUrn": "urn:li:fs_updateV2Actions:(urn:li:activity:7032115801016938496,MEMBER_SHARES,EMPTY,-,-)",
|
||||||
|
"dashEntityUrn": "urn:li:fsd_updateActions:(urn:li:activity:7032115801016938496,MEMBER_SHARES,EMPTY,urn:li:reason:-,urn:li:adCreative:-)"
|
||||||
|
},
|
||||||
|
"actionTriggerEnabled": false,
|
||||||
|
"detailPageType": "FEED_DETAIL",
|
||||||
|
"shareAudience": "PUBLIC",
|
||||||
|
"shareUrn": "urn:li:share:7032115800496848896",
|
||||||
|
"excludedFromSeen": false,
|
||||||
|
"trackingData": {
|
||||||
|
"requestId": "b25a7ec3-e28d-48c3-8992-6f2d239bb5ed",
|
||||||
|
"trackingId": "RG3qysJY+25QguPJBtnAPA=="
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"entityUrn": "urn:li:fs_updateV2:(urn:li:activity:7032115801016938496,MEMBER_SHARES,EMPTY,DEFAULT,false)",
|
||||||
|
"content": {
|
||||||
|
"com.linkedin.voyager.feed.render.ArticleComponent": {
|
||||||
|
"templateType": "DEFAULT",
|
||||||
|
"urn": "urn:li:article:7508397560141145240",
|
||||||
|
"swapTitleAndSubtitle": false,
|
||||||
|
"largeImage": {
|
||||||
|
"accessibilityTextAttributes": [],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"useCropping": false,
|
||||||
|
"sourceType": "VECTOR",
|
||||||
|
"vectorImage": {
|
||||||
|
"artifacts": [
|
||||||
|
{
|
||||||
|
"width": 800,
|
||||||
|
"fileIdentifyingUrlPathSegment": "800/0/1676589776974?e=1677236400&v=beta&t=LPc9sQwTqe_RKAxA-p-0PgvRKPsbZ9u2APJtFUdR8Ik",
|
||||||
|
"expiresAt": 1677236400000,
|
||||||
|
"height": 533
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 815,
|
||||||
|
"fileIdentifyingUrlPathSegment": "1280_800/0/1676589776974?e=1677236400&v=beta&t=GWvn4lgjCslPvTNHYUSs7C2dPdUQKg45m9kZv5hhw1A",
|
||||||
|
"expiresAt": 1677236400000,
|
||||||
|
"height": 543
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 160,
|
||||||
|
"fileIdentifyingUrlPathSegment": "160/0/1676589776974?e=1677236400&v=beta&t=ULTeCod2tkW1L4eFpuuwmzs-ol35N6ySsc7OcqtmoCU",
|
||||||
|
"expiresAt": 1677236400000,
|
||||||
|
"height": 106
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 480,
|
||||||
|
"fileIdentifyingUrlPathSegment": "480/0/1676589776974?e=1677236400&v=beta&t=-0Lf898wq0wcbhkxvQGq72rtNmuggxYVuUB6KKLBjSI",
|
||||||
|
"expiresAt": 1677236400000,
|
||||||
|
"height": 319
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rootUrl": "https://media.licdn.com/dms/image/sync/D4E27AQENdzDIiQ--jA/articleshare-shrink_"
|
||||||
|
},
|
||||||
|
"displayAspectRatio": 0.66625
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"saveAction": {
|
||||||
|
"entityUrn": "urn:li:fs_saveAction:(SAVE,urn:li:article:7508397560141145240)",
|
||||||
|
"saved": false,
|
||||||
|
"dashEntityUrn": "urn:li:fsd_saveState:(SAVE,urn:li:article:7508397560141145240)"
|
||||||
|
},
|
||||||
|
"subtitle": {
|
||||||
|
"textDirection": "USER_LOCALE",
|
||||||
|
"text": "theshiftproject.org • 5 min read"
|
||||||
|
},
|
||||||
|
"navigationContext": {
|
||||||
|
"trackingActionType": "viewLink",
|
||||||
|
"accessibilityText": "Open article: Le Shift recrute un.e Chef.fe de projet Agriculture ! [CDD 18 mois] by theshiftproject.org",
|
||||||
|
"actionTarget": "https://theshiftproject.org/article/offre-emploi-chef-projet-agriculture/"
|
||||||
|
},
|
||||||
|
"showSmallTitle": false,
|
||||||
|
"type": "EXTERNAL_FULL",
|
||||||
|
"title": {
|
||||||
|
"textDirection": "FIRST_STRONG",
|
||||||
|
"text": "Le Shift recrute un.e Chef.fe de projet Agriculture ! [CDD 18 mois]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commentary": {
|
||||||
|
"templateType": "DEFAULT",
|
||||||
|
"translationUrn": "urn:li:fs_translation:(urn:li:share:7032115800496848896,fr)",
|
||||||
|
"numLines": 3,
|
||||||
|
"text": {
|
||||||
|
"textDirection": "FIRST_STRONG",
|
||||||
|
"text": "Ce n'était pas le secteur le plus travaillé dans le Plan de Transformation de l'Economie Française, et donc le Shift Project a décidé d'y revenir. Car la production et cuisson de nos aliments, en incluant les émissions industrielles amont (fabrication des engrais, des intrants en général, et des machines agricoles), les émissions dans les exploitations, et celles à l'aval (industries agro-alimentaires, transports, emballages, déplacements des consommateurs pour faire les courses, cuisson, etc) c'est un tiers des émissions mondiales, et pas loin du même pourcentage en France.\n\nIl semble donc difficile de dire que nous allons devenir \"neutres\", ce qui suppose de supprimer la quasi-totalié de nos émissions actuelles, sans s'occuper sérieusement de ce secteur. C'est pour cela que The Shift Project se cherche le mouton ou la brebis à 5 pattes (c'est de circonstance) pour piloter la réflexion à venir sur la bonne manière de décarboner notre assiette.\n\nSi faire un petit tour par les locaux de cette association vous tente, à vos CV ! "
|
||||||
|
},
|
||||||
|
"originalLanguage": "French",
|
||||||
|
"dashTranslationUrn": "urn:li:fsd_translation:(urn:li:share:7032115800496848896,fr)"
|
||||||
|
},
|
||||||
|
"socialDetail": {
|
||||||
|
"dashEntityUrn": "urn:li:fsd_socialDetail:(urn:li:activity:7032115801016938496,urn:li:activity:7032115801016938496,urn:li:highlightedReply:-)",
|
||||||
|
"comments": {
|
||||||
|
"paging": {
|
||||||
|
"start": 0,
|
||||||
|
"count": 0,
|
||||||
|
"total": 25,
|
||||||
|
"links": []
|
||||||
|
},
|
||||||
|
"elements": []
|
||||||
|
},
|
||||||
|
"socialPermissions": {
|
||||||
|
"dashEntityUrn": "urn:li:fsd_socialPermissions:(urn:li:activity:7032115801016938496,urn:li:fsd_profile:ACoAACezhDYByiG-EGo31qm_ZSu2gSgyNAA1mZM)",
|
||||||
|
"canPostComments": true,
|
||||||
|
"entityUrn": "urn:li:fs_socialPermissions:(urn:li:activity:7032115801016938496,urn:li:fs_profile:(ACoAACezhDYByiG-EGo31qm_ZSu2gSgyNAA1mZM,en_US))",
|
||||||
|
"messagePermission": "PUBLIC",
|
||||||
|
"canShare": true,
|
||||||
|
"canReact": true
|
||||||
|
},
|
||||||
|
"showPremiumAnalytics": false,
|
||||||
|
"hideFirstPrompt": true,
|
||||||
|
"liked": false,
|
||||||
|
"showShareButton": true,
|
||||||
|
"totalShares": 0,
|
||||||
|
"urn": "urn:li:activity:7032115801016938496",
|
||||||
|
"threadId": "activity:7032115801016938496",
|
||||||
|
"allowedCommentersScope": "ALL",
|
||||||
|
"totalSocialActivityCounts": {
|
||||||
|
"socialDetailEntityUrn": "urn:li:fs_socialDetail:urn:li:activity:7032115801016938496",
|
||||||
|
"urn": "urn:li:activity:7032115801016938496",
|
||||||
|
"numComments": 33,
|
||||||
|
"dashEntityUrn": "urn:li:fsd_socialActivityCounts:urn:li:activity:7032115801016938496",
|
||||||
|
"reactionTypeCounts": [
|
||||||
|
{
|
||||||
|
"count": 243,
|
||||||
|
"reactionType": "LIKE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"count": 9,
|
||||||
|
"reactionType": "INTEREST"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"count": 4,
|
||||||
|
"reactionType": "PRAISE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"count": 2,
|
||||||
|
"reactionType": "APPRECIATION"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"count": 1,
|
||||||
|
"reactionType": "MAYBE"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entityUrn": "urn:li:fs_socialActivityCounts:urn:li:activity:7032115801016938496",
|
||||||
|
"numLikes": 259,
|
||||||
|
"liked": false
|
||||||
|
},
|
||||||
|
"entityUrn": "urn:li:fs_socialDetail:urn:li:activity:7032115801016938496",
|
||||||
|
"commentingDisabled": false,
|
||||||
|
"likes": {
|
||||||
|
"paging": {
|
||||||
|
"start": 0,
|
||||||
|
"count": 0,
|
||||||
|
"total": 259,
|
||||||
|
"links": []
|
||||||
|
},
|
||||||
|
"elements": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue