Skip to content

Commit 9969ab8

Browse files
Merge pull request #411 from DemocracyLab/linkedin_positions
Offering positions as an XML feed for LinkedIn's Limited Listings
2 parents a672029 + 5d009b5 commit 9969ab8

File tree

2 files changed

+40
-0
lines changed

2 files changed

+40
-0
lines changed

civictechprojects/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
url(r'^api/projects/recent', views.recent_projects_list),
6262
url(r'^api/projects', views.projects_list),
6363
url(r'^api/events', views.events_list),
64+
url(r'^api/limited_listings', views.limited_listings),
6465
url(r'^api/groups', views.groups_list),
6566
url(r'^api/my_projects', views.my_projects),
6667
url(r'^api/my_events', views.my_events),

civictechprojects/views.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,45 @@ def recent_projects_list(request):
581581
return JsonResponse({'projects': hydrated_project_list})
582582

583583

584+
def limited_listings(request):
585+
"""Summarizes current positions in a format specified by the LinkedIn "Limited Listings" feature."""
586+
587+
def cdata(str):
588+
# Using CDATA tags (and escaping the close sequence) protects us from XSS attacks when
589+
# displaying user provided string values.
590+
return f"<![CDATA[{str.replace(']]>', ']]]]><![CDATA[>')}]]>"
591+
592+
def position_to_job(position):
593+
project = position.position_project
594+
roleTag = Tag.get_by_name(position.position_role.first().slug)
595+
596+
return f"""
597+
<job>
598+
<company>{cdata(project.project_name)}</company>
599+
<title>{cdata(roleTag.display_name)}</title>
600+
<description>{cdata(position.position_description)}</description>
601+
<partnerJobId>{cdata(str(position.id))}</partnerJobId>
602+
<location>{cdata(", ".join([project.project_city, project.project_state]) if (project.project_city and project.project_state) else "")}</location>
603+
<city>{cdata(project.project_city)}</city>
604+
<state>{cdata(project.project_state)}</state>
605+
<country>{cdata(project.project_country)}</country>
606+
<applyUrl>{cdata(position.description_url or project.project_url)}</applyUrl>
607+
<industryCodes><industryCode>{cdata("4")}</industryCode></industryCodes>
608+
</job>
609+
"""
610+
611+
approved_projects = ProjectPosition.objects.filter(position_project__is_searchable=True)
612+
xml_response = f"""<?xml version="1.0" encoding="UTF-8"?>
613+
<source>
614+
<lastBuildDate>{timezone.now().strftime('%a, %d %b %Y %H:%M:%S %Z')}</lastBuildDate>
615+
<publisherUrl>https://www.democracylab.org</publisherUrl>
616+
<publisher>DemocracyLab</publisher>
617+
{"".join(map(position_to_job, approved_projects))}
618+
</source>"""
619+
620+
return HttpResponse(xml_response, content_type="application/xml")
621+
622+
584623
def apply_tag_filters(project_list, query_params, param_name, tag_filter):
585624
if param_name in query_params:
586625
tag_dict = get_tag_dictionary()

0 commit comments

Comments
 (0)